bytes() is a built-in Python type that creates an immutable sequence of bytes — the read-only counterpart to bytearray, perfect for binary data that should not be modified after creation (e.g., file contents, network packets, hashes, protocol messages). In 2026, bytes remains essential in data science (binary parsing, image/byte streams), software engineering (networking, crypto, serialization), and systems programming — offering hashability, efficient memory usage, and all read-only bytearray methods, with seamless integration into NumPy/Dask/Polars/pandas for array-based binary workflows.
Here’s a complete, practical guide to using bytes() in Python: creation from strings/iterables/integers, common operations & slicing, real-world patterns (earthquake binary metadata, log parsing, binary feature engineering), and modern best practices with type hints, performance, immutability, and integration with Dask/Polars/pandas/NumPy.
Creating bytes — from strings (with encoding), iterables, integers, or empty.
# From string (utf-8 by default)
b_str = bytes("Hello, ??!", "utf-8")
print(b_str) # b'Hello, \xe4\xb8\x96\xe7\x95\x8c!'
print(len(b_str)) # 18 bytes (Unicode chars take multiple bytes)
# From list of integers (0–255)
b_list = bytes([72, 101, 108, 108, 111, 44, 32, 87, 111, 114, 108, 100, 33])
print(b_list) # b'Hello, World!'
# From integer (length, initialized to 0)
b_zero = bytes(10)
print(b_zero) # b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
# Empty bytes
b_empty = bytes()
print(b_empty) # b''
Immutable operations & slicing — bytes supports all read-only methods of bytearray.
b = bytes(b"Hello")
print(b[0]) # 72 (ord('H'))
print(b[1:3]) # b'el'
# Common methods
print(b.decode('utf-8')) # 'Hello'
print(b.hex()) # '48656c6c6f'
print(b.count(b'l')) # 2
print(b.find(b'lo')) # 3
print(b.replace(b'H', b'J')) # b'Jello'
# Concatenation (creates new bytes)
b2 = b + b" World!"
print(b2) # b'Hello World!'
Real-world pattern: earthquake binary metadata processing — parse, extract, validate.
import struct
# Example: binary earthquake packet (simplified)
packet = bytes([
0x01, # version
0x07, 0x1A, # magnitude (float32 bytes)
0x23, 0x84, 0xA5, 0x40, # latitude
0xC7, 0x0F, 0xC9, 0xC0, # longitude
0x00, 0x00, 0xA0, 0x40 # depth
])
# Extract magnitude (bytes 1-4 as float)
mag_bytes = packet[1:5]
mag = struct.unpack('>f', mag_bytes)[0]
print(f"Magnitude: {mag:.2f}")
# Validate version
if packet[0] != 0x01:
raise ValueError(f"Invalid version: {packet[0]}")
# Create immutable copy for safe sharing
immutable_packet = bytes(packet)
# immutable_packet[0] = 0x02 # TypeError: 'bytes' object does not support item assignment
Best practices for bytes in Python & data workflows. Use bytes for immutability — when data should not change (hash keys, protocol messages). Modern tip: use Polars for tabular binary data — pl.col('binary').cast(pl.Binary); Dask for distributed binary streams. Prefer bytes(b'abc') — from bytes literal. Use bytes.fromhex('48656c6c6f') — from hex string. Use b.decode('utf-8') — convert back to string. Use struct — for packing/unpacking binary structures. Add type hints — def process_binary(b: bytes) -> None. Use memoryview(b) — zero-copy slicing/view. Use b.hex() — hex representation (Python 3.5+). Avoid large bytes — consider mmap for huge files. Profile memory — sys.getsizeof(b). Use b.replace(b'old', b'new') — binary replace. Use b.find(b'pattern') — search bytes. Use b == other_bytes — exact comparison. Use bytes(b) — copy existing bytes.
bytes() creates immutable byte sequences — from strings (with encoding), integers (0–255), length (zero-filled), or empty — perfect for binary data that should not change, hashing, and protocol handling. In 2026, use for immutable binary storage, parsing, and integrate with NumPy/Dask/Polars for array-based workflows. Master bytes, and you’ll handle binary data safely, efficiently, and immutably in any Python application.
Next time you need read-only bytes — use bytes(). It’s Python’s cleanest way to say: “Give me bytes that can’t be changed — safe for keys, protocols, and sharing.”