The bytes type, like str, is immutable. We can use index and slice notation on a bytes object and search for a particular sequence of bytes, but we can't extend or modify them. This can be very inconvenient when dealing with I/O, as it is often necessary to buffer incoming or outgoing bytes until they are ready to be sent. For example, if we are receiving data from a socket, it may take several recv calls before we have received an entire message.
This is where the bytearray built-in comes in. This type behaves something like a list, except it only holds bytes. The constructor for the class can accept a bytes object to initialize it. The extend method can be used to append another bytes object to the existing array (for example, when more data comes from a socket or other I/O channel).
Slice notation can be used on bytearray to modify the item inline. For example, this code constructs a bytearray from a bytes object and then replaces two bytes:
b = bytearray(b"abcdefgh") b[4:6] = b"\x15\xa3" print(b)
The output looks like this:
bytearray(b'abcd\x15\xa3gh')
If we want to manipulate a single element in bytearray, we must pass an integer between 0 and 255 (inclusive) as the value. This integer represents a specific bytes pattern. If we try to pass a character or bytes object, it will raise an exception.
A single byte character can be converted to an integer using the ord (short for ordinal) function. This function returns the integer representation of a single character:
b = bytearray(b"abcdef")
b[3] = ord(b"g")
b[4] = 68
print(b)
The output looks like this:
bytearray(b'abcgDf')
After constructing the array, we replace the character at index 3 (the fourth character, as indexing starts at 0, as with lists) with byte 103. This integer was returned by the ord function and is the ASCII character for the lowercase g. For illustration, we also replaced the next character up with byte number 68, which maps to the ASCII character for the uppercase D.
The bytearray type has methods that allow it to behave like a list (we can append integer bytes to it, for example), but also like a bytes object; we can use methods such as count and find the same way they would behave on a bytes or str object. The difference is that bytearray is a mutable type, which can be useful for building up complex sequences of bytes from a specific input source.