Функция bytearray() в Python: изменяемые байты
Функция bytearray() в Python создает изменяемую последовательность байтов, где каждый элемент - целое число от 0 до 255. В отличие от bytes, такой массив можно править на месте: заменять байты, дописывать и удалять фрагменты. Ниже разобраны параметры функции, способы создания, основные методы, перевод в текст и hex и задачи, где bytearray удобнее других типов.

Содержание статьи
Синтаксис и параметры
Функция встроенная и принимает до трех необязательных аргументов. Без аргументов она создает пустой массив.
bytearray(source=b'', encoding='utf-8', errors='strict')source- из чего строить массив: строка, целое число, итерируемый объект с числами или объект с буферным протоколом (например,bytes);encoding- кодировка, обязательна, еслиsourceявляется строкой;errors- что делать с символами, которые нельзя закодировать:'strict'(ошибка),'replace'(знак вопроса),'ignore'(пропустить).
Тип аргумента source полностью меняет поведение функции:
| source | Пример | Результат |
|---|---|---|
| строка + кодировка | bytearray('hi', 'utf-8') | bytearray(b'hi') |
| целое число | bytearray(3) | bytearray(b'\x00\x00\x00') |
| список чисел 0-255 | bytearray([72, 105]) | bytearray(b'Hi') |
| bytes или другой буфер | bytearray(b'abc') | копия данных |
| ничего | bytearray() | bytearray(b'') |
Создание из строки
Строка в Python хранит символы Unicode, а не байты, поэтому без кодировки ее не во что превратить. Вызов bytearray('abc') выбросит TypeError: string argument without an encoding.
data = bytearray('Привет', 'utf-8')
print(data)
# bytearray(b'\xd0\x9f\xd1\x80\xd0\xb8\xd0\xb2\xd0\xb5\xd1\x82')
print(len(data)) # 12
print(bytearray('ёж', 'ascii', errors='replace')) # bytearray(b'??')Слово из шести букв заняло 12 байт: в UTF-8 каждая кириллическая буква кодируется двумя байтами. Байты вне диапазона ASCII Python показывает в виде \xNN. О кодировках и ошибках их преобразования рассказывает статья Python кодировка строки.
Создание из числа и последовательности
Целое число задает размер массива, заполненного нулевыми байтами. Это удобно, когда нужен буфер заранее известной длины, который потом заполняется данными.
print(bytearray(5)) # bytearray(b'\x00\x00\x00\x00\x00')
print(bytearray([2, 3, 5, 7])) # bytearray(b'\x02\x03\x05\x07')
print(bytearray(range(65, 70))) # bytearray(b'ABCDE')Из итерируемого объекта берутся числа, и каждое должно быть в диапазоне 0-255. Значение 256 или -1 вызовет ValueError: byte must be in range(0, 256). Числа от 32 до 126 Python выводит как печатные символы ASCII, поэтому range(65, 70) отображается буквами.
Изменение содержимого на месте
Изменяемость - главная причина выбрать bytearray. Индексация возвращает целое число, а присваивание по индексу ожидает целое число. Срезы, наоборот, работают с байтовыми последовательностями.
b = bytearray(b'hello')
b[0] = 72 # код буквы H
b.append(33) # добавить один байт: '!'
b.extend(b' ok') # добавить несколько байтов
print(b) # bytearray(b'Hello! ok')
b[0:5] = b'HELLO' # замена среза
del b[-3:] # удаление среза
print(b) # bytearray(b'HELLO!')
print(b[0], b[0:1]) # 72 bytearray(b'H')Кроме списочных методов (append, extend, insert, pop, remove, clear, reverse), у bytearray есть почти все методы строк: find(), replace(), split(), strip(), upper(), startswith(). Они принимают байтовые аргументы, например b.replace(b'l', b'L'), и возвращают новый объект, исходный при этом не меняется.
Перевод в строку, bytes и hex
Обратно в текст массив переводит метод decode() с той же кодировкой, в которой данные были записаны. Неизменяемую копию дает функция bytes(), а шестнадцатеричное представление - метод hex().
b = bytearray('Привет', 'utf-8')
print(b.decode('utf-8')) # Привет
print(bytes(b)) # b'\xd0\x9f...' неизменяемая копия
raw = bytearray.fromhex('de ad be ef')
print(raw) # bytearray(b'\xde\xad\xbe\xef')
print(raw.hex()) # deadbeefЕсли декодировать байты не той кодировкой, будет UnicodeDecodeError или нечитаемый текст. Метод fromhex() пропускает пробелы между парами цифр, поэтому удобен для разбора дампов из документации и отладчиков.
bytearray или bytes
Оба типа хранят байты и поддерживают одинаковые операции чтения. Разница только в изменяемости, и из нее следуют остальные отличия.
| Свойство | bytes | bytearray |
|---|---|---|
| Изменение по индексу | нет, TypeError | да |
| Литерал | b'abc' | нет, только вызов функции |
| Ключ словаря, элемент множества | да | нет, тип нехешируемый |
| Подходит для | констант, ключей, передачи данных | буферов, сборки и правки данных |
Когда двоичные данные собираются из множества кусков, например пакет для сетевого протокола или заголовок файла, bytearray выгоднее: дописывание в него не создает каждый раз новый объект, как конкатенация bytes. Готовый результат при необходимости превращают в bytes одним вызовом. Для чтения файла прямо в заранее созданный буфер служит метод readinto() у файлов, открытых в двоичном режиме, подробнее в статье про бинарные файлы.
Частые вопросы
Ответы на вопросы, которые задают при знакомстве с bytearray.
Почему b[0] возвращает число, а не символ?
Элемент байтовой последовательности - это целое число от 0 до 255. Чтобы получить последовательность из одного байта, используйте срез b[0:1], а символ - chr(b[0]) для ASCII-данных.
Как очистить bytearray?
Вызовите метод clear() или удалите все элементы через del b[:]. Оба способа очищают тот же объект, поэтому изменения увидят все переменные, которые на него ссылаются.
Можно ли записать bytearray в файл?
Да, откройте файл в двоичном режиме 'wb' и передайте массив в write(). Строку записывать так нельзя, а bytearray и bytes подходят напрямую.
Как сравнить bytearray и bytes?
Оператор == сравнивает содержимое, поэтому bytearray(b'ab') == b'ab' дает True, хотя типы объектов разные.