Python Bytes Literal Explained with Examples
python bytes literal: Learn how to write and use Python bytes literals, including syntax, encoding, and common pitfalls when working with binary data.
When you need to work with binary data in Python, the bytes literal is the syntax you'll use most often. A Python bytes literal is a sequence of bytes written with a b prefix, like b'hello' or b"hello". It produces a bytes object that is immutable and behaves differently from a str in important ways. This article explains the syntax, how bytes literals encode data, and the practical decisions around using them in your code.
Bytes Literal Syntax
A bytes literal is created by placing a b or B prefix before a string literal. The B prefix is rarely used, but it is valid. The literal can be enclosed in single quotes, double quotes, or triple quotes for multi-line values.
data = b'hello' data2 = b"world" data3 = b'''line1 line2''' data4 = b"""line1 line2"""
The content inside a bytes literal must be ASCII characters. If you need to represent a byte value outside the ASCII range, use an escape sequence. The most common escape is \xHH, where HH is a two-digit hexadecimal number from 00 to FF.
byte_with_value_0 = b'\x00' byte_with_value_255 = b'\xff'
You can also use standard escapes like \n for newline, \t for tab, and \\ for a backslash. These escapes are interpreted to produce the corresponding byte values.
escaped = b'line1\nline2'
A bytes literal cannot contain a non-ASCII character directly. For example, b'café' raises a SyntaxError because é is not an ASCII character. To represent the UTF-8 encoding of café, you must write the escape sequence b'caf\xc3\xa9' or use a string and call .encode().
Bytes vs String Literals
The most important distinction is that a bytes object is a sequence of integers in the range 0–255, while a str object is a sequence of Unicode code points. This difference affects how you index, slice, and iterate over the values.
| Operation | bytes | str |
|---|---|---|
| Indexing | Returns an int (byte value) | Returns a one-character str |
| Slicing | Returns a new bytes object | Returns a new str |
| Iteration | Yields integers | Yields characters |
| Length | Number of bytes | Number of code points |
b = b'abc' print(b[0]) # 97 print(b[0:2]) # b'ab' s = 'abc' print(s[0]) # 'a' print(s[0:2]) # 'ab'
Bytes objects are immutable, just like strings. Any operation that appears to modify a bytes object actually creates a new one.
Encoding and Decoding Bytes
Bytes literals are raw data; they do not carry an encoding. When you write b'hello', the ASCII characters are directly mapped to their byte values. For text that includes non-ASCII characters, you must explicitly encode a string to bytes using the .encode() method, and decode bytes back to a string using .decode().
text = 'café' encoded = text.encode('utf-8') print(encoded) # b'caf\xc3\xa9' decoded = encoded.decode('utf-8') print(decoded) # 'café'
The encoding you choose matters. The same string produces different bytes under different encodings. For example, 'café' encodes to b'caf\xe9' in Latin-1 but b'caf\xc3\xa9' in UTF-8. When you decode, you must use the same encoding that was used to encode the data.
Common Mistakes with Bytes Literals
One of the most frequent errors is trying to combine a str and a bytes object. Python does not implicitly convert between them.
b = b'hello' s = 'world' # b + s -> TypeError: can't concat str to bytes
To combine them, you must explicitly convert one side. Either decode the bytes to a string, or encode the string to bytes.
b = b'hello' s = 'world' combined = b + s.encode('utf-8') # b'helloworld'
Another mistake is writing a non-ASCII character directly in a bytes literal. This raises a SyntaxError at parse time. Always use \xHH escapes or start from a string and encode.
A third issue occurs when you read from a binary file and then try to use the result as if it were text. The read() method on a binary file returns bytes, so you must decode it if you need a string.
with open('data.bin', 'rb') as f: raw = f.read() # bytes text = raw.decode('utf-8') # if the file contains text
Working with Binary Data
Bytes literals are the foundation for working with binary protocols, file formats, and cryptographic operations. For example, when writing to a binary file, you must pass a bytes-like object.
with open('output.bin', 'wb') as f: f.write(b'\x00\x01\x02')
When reading from a network socket, the data arrives as bytes. You can parse it by slicing and indexing the bytes object directly.
packet = b'\x01\x02\x03\x04' version = packet[0] length = packet[1] payload = packet[2:]
Hashing functions in the hashlib module accept bytes, not strings. So you must encode your input before hashing.
import hashlib digest = hashlib.sha256(b'password').hexdigest()
Bytes Literals and Python Versions
The behavior described here applies to Python 3. In Python 2, b'...' was treated as a plain string literal, and bytes was an alias for str. This caused confusion because the same syntax meant different things in the two major versions. Python 3 cleanly separates text (str) from binary data (bytes), and the b prefix is the explicit marker for binary data. If you are maintaining code that must run on both Python 2 and 3, you need to handle the differences carefully, but for new code targeting Python 3, the rules above apply consistently.
Performance and Memory Considerations
Bytes objects are compact because each element is a single byte. This makes them efficient for storing and transmitting binary data. Strings, on the other hand, store Unicode code points, which can require multiple bytes per character in memory depending on the internal representation. When you are processing raw data that does not need text interpretation, using bytes avoids the overhead of encoding and decoding at every step.
Slicing a bytes object creates a new bytes object, which copies the referenced data. If you need to extract many small slices from a large buffer, this can lead to memory overhead. In such cases, consider using memoryview to avoid copies, but that is a more advanced topic. For most applications, the simplicity of bytes literals and slicing is worth the occasional copy.
When to Use Bytes Literals
Use a bytes literal when you are dealing with data that is inherently binary: file contents read in binary mode, network packets, cryptographic keys, or serialized data. Use a string literal when you are working with human-readable text. If you have text that must be written to a binary file or sent over a network, encode it to bytes at the boundary. Conversely, when you receive bytes and need to treat them as text, decode them immediately.
The decision is not about performance alone; it is about the type of data you are handling. Mixing the two types without explicit conversion leads to errors and bugs. By making the boundary explicit with encode() and decode(), you keep your code predictable and maintainable.