Back to Blog
Python

Python bytearray Type: Mutable Binary Data

python bytearray type: Learn how to use Python's bytearray type for mutable binary data: creation, mutation, conversions, performance, and common pitfalls.

bytearrayPythonmutable bytesbinary databytes vs bytearray
A bytearray visualized as a mutable sequence of bytes with an edit pencil, representing in-place binary data modification.

The python bytearray type provides a mutable sequence of integers in the range 0–255. Unlike bytes, which is immutable, bytearray allows in-place modification of the underlying binary data. This makes it useful when you need to build or alter binary buffers without creating new objects on every change.

The bytearray Type and Its Core Behavior

A bytearray behaves like a list of small integers, but each element is constrained to a single byte. When you access an element, you get an int; when you slice, you get a new bytearray. This distinction matters for operations like comparison and concatenation.

ba = bytearray(b'hello') print(ba[0]) # 104 print(ba[0:2]) # bytearray(b'he')

The object itself is iterable, and you can pass it to functions that expect a bytes-like object. Many binary protocols and file APIs accept bytearray directly, so you rarely need to convert it to bytes unless you need an immutable representation.

Creating a bytearray

You can create a bytearray from a bytes literal, a string with an encoding, an iterable of integers, or a preallocated size.

# From bytes ba1 = bytearray(b'abc') # From a string with encoding ba2 = bytearray('abc', 'utf-8') # From an iterable of integers ba3 = bytearray([104, 101, 108, 108, 111]) # Preallocate 10 zero bytes ba4 = bytearray(10)

The last form is useful when you know the buffer size in advance and plan to fill it later. It avoids repeated resizing and is common in protocol parsing where you read chunks into a fixed buffer.

Mutating a bytearray

The defining feature of bytearray is in-place mutation. You can assign to an index, append, extend, and delete slices just like a list.

ba = bytearray(b'hello') ba[0] = 72 # ASCII 'H' ba.append(33) # '!' ba.extend(b' world') # append multiple bytes ba[1:3] = b'EL' # replace slice print(ba) # bytearray(b'HELlo world!')

Index assignment requires an integer between 0 and 255. Slice assignment accepts any bytes-like object, including another bytearray or a bytes object. This flexibility makes bytearray a convenient accumulator when building binary messages incrementally.

bytearray vs bytes: When to Use Which

The choice between bytearray and bytes comes down to mutability and hashability. bytes is immutable and hashable, so it can be used as a dictionary key or stored in a set. bytearray is mutable and unhashable, so it cannot be used directly in those contexts.

Propertybytesbytearray
MutabilityImmutableMutable
HashableYesNo
Index accessReturns intReturns int
Slice accessReturns bytesReturns bytearray
Use caseFixed binary dataIncremental construction

Use bytes when you have a constant binary payload, such as a cryptographic key or a file signature. Use bytearray when you need to modify the data in place, especially in loops that would otherwise create many intermediate bytes objects.

Converting Between bytearray, bytes, and Strings

Conversion between bytearray and bytes is cheap because both share the same underlying buffer concept, though bytes is immutable. To get an immutable copy, use bytes(ba).

ba = bytearray(b'data') b = bytes(ba)

To convert to a string, decode using an encoding. The reverse requires encoding a string.

ba = bytearray(b'hello') text = ba.decode('utf-8') ba2 = bytearray('hello', 'utf-8')

You can also create a memoryview over a bytearray to avoid copying when passing it to APIs that support buffer protocol.

ba = bytearray(1024) view = memoryview(ba) # view[0] = 255 works and modifies ba

This is useful in high-performance scenarios where you want to avoid duplicating large buffers.

Performance and Memory Considerations

Because bytearray is mutable, it can be more memory-efficient than repeatedly concatenating bytes objects. Each concatenation of bytes creates a new object and copies the old data. With bytearray, you can extend in place, avoiding that copy when the underlying buffer has capacity.

However, bytearray is not a magic bullet. It has more overhead than bytes for read-only operations because it tracks mutability and may need to resize. If you never modify the data after creation, bytes is lighter and can be shared safely across threads.

For large binary data, consider using memoryview to avoid copies when slicing. A slice of a bytearray returns a new bytearray, which copies the data. A memoryview slice references the original buffer, reducing memory usage.

ba = bytearray(b'0123456789') view = memoryview(ba) sub = view[2:5] # references ba, no copy sub[0] = 88 # modifies ba print(ba) # bytearray(b'0183456789')

This behavior is critical when processing large binary files or network packets where copying would dominate runtime.

Common Pitfalls and Edge Cases

One frequent mistake is assuming that indexing returns a bytes object. It returns an int, so comparing it to a bytes literal fails.

ba = bytearray(b'abc') if ba[0] == b'a': # False, because b'a' is bytes pass # Correct: compare to integer if ba[0] == 97: pass ```n Another pitfall is using `bytearray` as a dictionary key. Since it is mutable, it cannot be hashed. If you need a mutable buffer that also serves as a key, you must convert it to `bytes` first, which creates a copy. When preallocating with `bytearray(n)`, all elements are initialized to zero. This is fine for many use cases, but if you plan to fill the entire buffer, you might not need the zero-initialization overhead. In CPython, the allocation is fast, but for very large buffers, the zero fill takes time. Finally, be careful when passing a `bytearray` to a function that expects a read-only bytes-like object. Most standard library functions accept it, but some C extensions may require `bytes` explicitly. In those cases, a conversion is necessary, and you should weigh the cost of that copy against the benefits of mutability. For protocol handling, a common pattern is to read data into a `bytearray`, parse it, and then extract immutable `bytes` slices for hashing or storing. This combines the efficiency of in-place parsing with the safety of immutable keys.
python bytearray type: Practical Usage and Code Examples | RYUSLOG DEV