Back to Blog
Python

Python bytearray: Mutable Bytes for Binary Data

python bytearray: Learn how to create, modify, and convert Python bytearray objects for efficient binary data handling. Understand mutable bytes, memory usage, and pra...

bytearraybytesbinary datamutable sequencememoryview
Diagram showing a bytearray as a mutable sequence of bytes with modification arrows

python bytearray requires a clear understanding of the core syntax, runtime behavior, and practical implementation patterns demonstrated in the examples below.

Python's bytearray type provides a mutable sequence of bytes, useful when you need to modify binary data in place. Unlike the immutable bytes type, bytearray allows you to change elements, append, extend, and slice-assign, making it a practical choice for protocol handling, image processing, and other low-level data manipulation.

What Is a bytearray in Python?

A bytearray is a built-in type that represents a mutable sequence of integers in the range 0–255. Each element is a single byte. It behaves like a list of integers, but with the constraint that every value must fit in one byte. This makes it ideal for working with raw binary data, file I/O, network buffers, and any situation where you need to mutate byte-level information without the overhead of creating new immutable bytes objects.

The mutability of bytearray is its defining characteristic. While bytes is immutable and hashable, bytearray is not hashable, but it can be used as a buffer that you modify in place. This distinction matters when choosing a type for a particular task.

Creating bytearray Objects

You can create a bytearray in several ways, depending on the source data.

From an Iterable of Integers

Pass a list or tuple of integers between 0 and 255:

ba = bytearray([65, 66, 67]) print(ba) # bytearray(b'ABC')

From a String with Encoding

When you have text, you must specify an encoding to convert it to bytes:

ba = bytearray('hello', 'utf-8') print(ba) # bytearray(b'hello')

From a bytes Object

Copy an existing bytes or bytearray:

original = b'\x00\x01\x02' ba = bytearray(original) print(ba) # bytearray(b'\x00\x01\x02')

From a Size (Zero-Filled)

Create a bytearray of a given length, filled with zero bytes:

ba = bytearray(4) print(ba) # bytearray(b'\x00\x00\x00\x00')

This is useful when you need a preallocated buffer that you will fill later.

Modifying bytearray Content

Because bytearray is mutable, you can change its contents using index assignment, slicing, and methods like append, extend, insert, pop, and remove.

Index Assignment

Assign a new byte value to a specific position:

ba = bytearray(b'abc') ba[0] = 120 # 'x' print(ba) # bytearray(b'xbc')

The assigned value must be an integer in the range 0–255. Assigning anything else raises ValueError.

Slice Assignment

Replace a slice with another sequence of bytes:

ba = bytearray(b'abcdef') ba[1:3] = b'XYZ' print(ba) # bytearray(b'aXYZdef')

The replacement can be any iterable of integers or a bytes-like object. The slice length does not need to match the replacement length; the bytearray will grow or shrink accordingly.

Appending and Extending

Add single bytes or multiple bytes at the end:

ba = bytearray(b'abc') ba.append(100) # 'd' ba.extend(b'ef') print(ba) # bytearray(b'abcdef')

append takes an integer, while extend accepts an iterable of integers or a bytes-like object.

Inserting and Removing

Insert a byte at a specific index, or remove by value:

ba = bytearray(b'abc') ba.insert(1, 120) # 'x' print(ba) # bytearray(b'axbc') ba.remove(120) # removes the first occurrence of 120 print(ba) # bytearray(b'abc')

pop removes and returns the last byte (or a specific index if provided).

Converting Between bytes, bytearray, and Other Types

You often need to move between bytes, bytearray, and text. The conversion methods are straightforward.

bytes to bytearray and Back

b = b'\x00\x01\x02' ba = bytearray(b) back = bytes(ba) print(back == b) # True

Converting a bytearray to bytes creates a new immutable copy. This is a common step before sending data over a socket or writing to a file, where immutability is preferred.

bytearray to String

Use .decode() with an appropriate encoding:

ba = bytearray(b'hello') s = ba.decode('utf-8') print(s) # 'hello'

Hexadecimal Representation

For debugging or logging, .hex() returns a string of hex digits:

ba = bytearray(b'\x00\x0f\xff') print(ba.hex()) # '000ffff'

Using memoryview

A memoryview allows you to access the buffer without copying, which is useful for zero-copy operations:

ba = bytearray(b'\x01\x02\x03') mv = memoryview(ba) mv[0] = 9 print(ba) # bytearray(b'\x09\x02\x03')

Changes made through the memoryview are reflected in the original bytearray because they share the same underlying buffer.

bytes vs bytearray: Choosing the Right Type

The choice between bytes and bytearray depends on whether you need to modify the data after creation. The table below summarizes the key differences.

Propertybytesbytearray
MutabilityImmutableMutable
HashableYesNo
Memory overheadSlightly lowerSlightly higher
Use caseRead-only data, dict keysIn-place modification
Thread safetySafe to shareRequires external locking

Use bytes when the data will not change after creation, such as a fixed header or a cryptographic key. Use bytearray when you need to parse and modify a buffer incrementally, like building a network packet or editing a binary file in memory.

Performance and Memory Considerations

bytearray avoids the cost of repeatedly creating new bytes objects when you need to change parts of a buffer. Modifying a bytearray in place is generally faster than concatenating or slicing bytes objects, especially for large data. However, bytearray objects have a small memory overhead compared to bytes because they track capacity and length separately.

When you convert a bytearray to bytes (e.g., to pass to an API that expects immutability), a copy is made. If you need to avoid that copy, consider using memoryview to expose the buffer without copying.

One performance pitfall is resizing. Appending or inserting can trigger reallocation when the internal capacity is exceeded. If you know the final size in advance, preallocate with bytearray(size) and then fill it via slice assignment to avoid repeated resizing.

Common Use Cases for bytearray

bytearray shines in scenarios that require low-level binary manipulation.

Network Protocol Buffers

When building a custom protocol, you often need to assemble a header, modify fields, and send the result. A bytearray lets you update fields in place:

packet = bytearray(20) # preallocate packet[0:4] = (0x12345678).to_bytes(4, 'big') packet[4:6] = (100).to_bytes(2, 'big') # send over socket sock.sendall(bytes(packet))

Binary File Editing

Reading a file into a bytearray, modifying specific offsets, and writing it back is efficient:

with open('image.bin', 'r+b') as f: data = bytearray(f.read()) data[10:14] = b'\x00\x00\x00\x00' # clear a field f.seek(0) f.write(data)

Image Processing

Pixel data can be manipulated directly as a byte array, especially for formats like BMP or raw RGB:

pixels = bytearray(width * height * 3) # RGB for i in range(0, len(pixels), 3): pixels[i] = 255 - pixels[i] # invert red channel

Handling Edge Cases and Errors

Working with bytearray introduces a few common pitfalls.

Value Out of Range

Assigning an integer outside 0–255 raises ValueError:

ba = bytearray(1) ba[0] = 256 # ValueError: byte must be in range(0, 256)

Index Out of Range

Like lists, accessing or assigning an invalid index raises IndexError.

Encoding Errors

When creating a bytearray from a string, an unknown character for the specified encoding raises UnicodeEncodeError. Always use an encoding that can represent the string, or handle the error explicitly.

Mutability and Hashability

Because bytearray is mutable, it cannot be used as a dictionary key or stored in a set. If you need a hashable representation, convert it to bytes first.

Concurrency

If multiple threads modify the same bytearray, you must synchronize access with a lock. The mutable nature makes it unsafe for concurrent writes without external coordination.

Understanding these edge cases helps you avoid subtle bugs when using bytearray in production code. The type is a powerful tool for binary data, but it requires care with boundaries and concurrency.

python bytearray: Practical Usage and Code Examples | RYUSLOG DEV