~22 min · bytes, bytearray, memoryview, encoding, binary
Level 0Curious
0 XP0/93 lessons0/23 achievements
0/100 XP to next level100 XP to go0% complete
The track most tutorials skip — and you can't
Sooner or later you'll touch raw bytes: reading a binary file, decoding a network message, hashing a password, sending an HTTP body. Python has three types in the binary family — bytes (immutable), bytearray (mutable), and memoryview (a zero-copy window into binary data). Most tutorials gloss over them; cwkPippa's adapters touch all three.
str vs bytes — Python 3's hard line
In Python 3, str is a sequence of Unicode characters. bytes is a sequence of raw 8-bit numbers. They're not interchangeable. You convert str → bytes by encoding (default: UTF-8), and bytes → str by decoding. The b-prefix literal b"hello" is bytes, no encoding needed. Mixing them in concatenation is a TypeError, and that's the language saving you from a class of bugs that haunted Python 2 forever.
Indexing bytes returns an integer
One of the most surprising bits: b"abc"[0] is 97, not b"a". Iterating bytes also yields integers. If you want the single-byte object, slice instead: b"abc"[0:1]. This is consistent with the "bytes are numbers" mental model, but it bites people who expect string-like behavior.
bytearray — when bytes need to mutate
bytes is immutable — like a frozen string. bytearray is the mutable cousin. You can do ba[i] = 0xFF, ba.append(byte), del ba[i]. It's used for buffers you fill in incrementally — receiving a network message, building a binary file in memory before flushing.
memoryview — slicing without copying
Slicing bytes makes a copy. For a 10MB buffer with thousands of slices in a parser loop, that copying murders your performance. memoryview wraps the same buffer with a no-copy interface: slicing returns another view into the same memory. This is the trick libraries like struct, numpy, and the binary protocol parsers reach for. You probably won't write memoryview code on day one, but you'll see it in serious libraries — and now you'll know what it is.
Principle: When in doubt, encode/decode at the boundaries. Inside your program, work with str. The moment you write to a socket, file, or hash function, encode to bytes. The moment you read from one, decode back. This pattern keeps the rest of your code clean.
Code
Encoding and decoding — the bridge between str and bytes·python
# str -> bytes (encode)
text = "안녕, 아빠"
b = text.encode("utf-8")
print(b) # b'\xec\x95\x88\xeb\x85\x95, \xec\x95\x84\xeb\xb9\xa0'
print(len(b)) # 14 — UTF-8 uses 3 bytes per Korean char
print(len(text)) # 6 — Python counts characters
# bytes -> str (decode)
back = b.decode("utf-8")
print(back == text) # True
# Wrong encoding raises
try:
b.decode("ascii")
except UnicodeDecodeError as e:
print("failed:", e)
Indexing bytes gives integers — surprise!·python
data = b"hello"
print(data[0]) # 104 — int!
print(data[0:1]) # b'h' — slice gives a bytes
# Iterating yields ints
for byte in b"abc":
print(byte)
# 97
# 98
# 99
# To get characters, decode first
for ch in b"abc".decode():
print(ch)
# a
# b
# c
Bytes literals — escapes and the b prefix·python
# b'' literal — direct bytes, no encoding step
raw = b"\x00\x01\x02\xff"
print(raw) # b'\x00\x01\x02\xff'
print(list(raw)) # [0, 1, 2, 255]
# Mixing str and bytes is a TypeError
try:
"hello " + b"world"
except TypeError as e:
print(e) # can only concatenate str (not 'bytes') to str
# bytes.fromhex / .hex() — round-trip with hex
h = b"\xde\xad\xbe\xef".hex()
print(h) # 'deadbeef'
print(bytes.fromhex("deadbeef")) # b'\xde\xad\xbe\xef'
bytearray — for incremental binary building·python
data = bytearray(b"hello world" * 1000) # ~11KB buffer
# Slicing bytes/bytearray COPIES
slice_copy = data[100:200]
# memoryview gives a no-copy view
mv = memoryview(data)
slice_view = mv[100:200]
# Reading is identical
print(bytes(slice_view[:5])) # b'orld '
# Mutating through the view writes to the original buffer
slice_view[0] = 0x58 # 'X'
print(data[100:105]) # bytearray(b'Xrld ')
# Why this matters: parsing 10MB of data and slicing thousands of times
# stays cheap because no copies happen.
mv.release()
(a) Take the string "피파, 안녕?" and encode to bytes using UTF-8. Print the bytes object, its length, and the length of the original string. Why are they different? (b) Build a bytearray from scratch by appending the bytes for "hello", then a NUL byte (0x00), then "world". Print the result. (c) Take any 1KB+ bytes object, wrap it in memoryview, and verify a slice of the memoryview reads the same bytes as a slice of the original. (Bonus: time both slices a million times and compare.)
Progress
Progress is local-only — sign in to sync across devices.