На этом шаге мы рассмотрим назначение, задание и использование этого типа данных.
Тип данных str отлично подходит для хранения текстовой информации, но что делать, если необходимо обрабатывать изображения? Ведь изображение не имеет кодировки, а значит, оно не может быть преобразовано в Unicode-строку. Для решения этой проблемы в Python 3 были введены типы bytes и bytearray, которые позволяют хранить последовательность целых чисел в диапазоне от 0 до 255. Каждое такое число обозначает код символа. Тип данных bytes относится к неизменяемым типам, как и строки, а тип данных bytearray - к изменяемым, как и списки.
Создать объект типа bytes можно следующими способами:
>>> bytes() b'' >>> bytes("строка", "cp1251") b'\xf1\xf2\xf0\xee\xea\xe0' >>> bytes("строка") Traceback (most recent call last): File "<pyshell#2>", line 1, in <module> bytes("строка") TypeError: string argument without an encoding
В третьем параметре могут быть указаны значения "strict" (при ошибке возбуждается исключение UnicodeEncodeError - значение по умолчанию), "replace" (неизвестный символ заменяется символом вопроса) или "ignore" (неизвестные символы игнорируются). Пример:
>>> bytes("string\uFFFD", "cp1251", "strict") Traceback (most recent call last): File "<pyshell#4>", line 1, in <module> bytes("string\uFFFD", "cp1251", "strict") File "C:\Python38-32\lib\encodings\cp1251.py", line 12, in encode return codecs.charmap_encode(input,errors,encoding_table) UnicodeEncodeError: 'charmap' codec can't encode character '\ufffd' in position 6: character maps to <undefined> >>> bytes("string\uFFFD", "cp1251", "replace") b'string?' >>> bytes("string\uFFFD", "cp1251", "ignore") b'string'
>>> "строка".encode() b'\xd1\x81\xd1\x82\xd1\x80\xd0\xbe\xd0\xba\xd0\xb0' >>> "строка".encode(encoding="cp1251") b'\xf1\xf2\xf0\xee\xea\xe0' >>> "строка\uFFFD".encode(encoding="cp1251", errors="xmlcharrefreplace") b'\xf1\xf2\xf0\xee\xea\xe0�' >>> "строка\uFFFD".encode(encoding="cp1251", errors="backslashreplace") b'\xf1\xf2\xf0\xee\xea\xe0\\ufffd'
>>> b"string", b'string', b"""string""", b'''string''' (b'string', b'string', b'string', b'string') >>> b"строка" SyntaxError: bytes can only contain ASCII literal characters. >>> b"\xf1\xf2\xf0\xee\xea\xe0" b'\xf1\xf2\xf0\xee\xea\xe0'
>>> b = bytes([225, 226, 224, 174, 170, 160]) >>> b b'\xe1\xe2\xe0\xae\xaa\xa0' >>> str(b, "cp866") 'строка'
>>> bytes(10) b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
>>> b = bytes.fromhex(" e1 e2e0ae aaa0") >>> b b'\xe1\xe2\xe0\xae\xaa\xa0' >>> str(b, "cp866") 'строка'
Объекты типа bytes относятся к последовательностям. Каждый элемент такой последовательности может хранить целое число от 0 до 255, которое обозначает код символа. Как и все последовательности, объекты поддерживают обращение к элементу по индексу, получение среза, конкатенацию, повторение и проверку на вхождение:
>>> b = bytes("string", "cp1251") >>> b b'string' >>> b[0] # Обращение по индексу 115 >>> b[1:3] # Получение среза b'tr' >>> b + b"123" # Конкатенация b'string123' >>> b * 3 # Повторение b'stringstringstring' >>> 115 in b, b"tr" in b, b"as" in b (True, True, False)
Как видно из примера, при выводе объекта целиком, а также при извлечении среза, производится попытка отображения символов. Однако доступ по индексу возвращает целое число, а не символ. Если преобразовать объект в список, то мы получим последовательность целых чисел:
>>> list(bytes("string", "cp1251")) [115, 116, 114, 105, 110, 103]
Тип bytes относится к неизменяемым типам. Это означает, что можно получить значение по индексу, но изменить его нельзя:
>>> b = bytes("string", "cp1251") >>> b[0] = 168 Traceback (most recent call last): File "<pyshell#4>", line 1, in <module> b[0] = 168 TypeError: 'bytes' object does not support item assignment
Объекты типа bytes поддерживают большинство строковых методов, которые мы рассматривали на предыдущих шагах. Однако некоторые из этих методов могут некорректно работать с русскими буквами - в этих случаях следует использовать тип str, а не тип bytes. Не поддерживаются объектами типа bytes строковые методы encode(), isidentifier(), isprintable(), isnumeric(), isdecimal(), format_map() и format(), а также операция форматирования.
При использовании методов следует учитывать, что в параметрах нужно указывать объекты типа bytes, а не строки:
>>> b = bytes("string", "cp1251") >>> b.replace(b"s", b"S") b'String'
Необходимо также помнить, что смешивать строки и объекты типа bytes в выражениях нельзя. Предварительно необходимо явно преобразовать объекты к одному типу, а лишь затем производить операцию:
>>> b"string" + "string" Traceback (most recent call last): File "<pyshell#11>", line 1, in <module> b"string" + "string" TypeError: can't concat str to bytes >>> b"string" + "string".encode('ascii') b'stringstring'
Объект типа bytes может содержать как однобайтовые символы, так и многобайтовые. При использовании многобайтовых символов некоторые функции могут работать не так, как вы думаете, - например, функция len() вернет количество байтов, а не символов:
>>> len("строка") 6 >>> len(bytes("строка", "cp1251")) 6 >>> len(bytes("строка", "utf-8")) 12
Преобразовать объект типа bytes в строку позволяет метод decode(). Метод имеет следующий формат:
decode([encoding="utf-8"][, errors="strict"])
Параметр encoding задает кодировку символов (по умолчанию UTF-8) в объекте bytes, a параметр errors - способ обработки ошибок при преобразовании. В параметре errors можно указать значения "strict" (значение по умолчанию), "replace" или "ignore". Пример преобразования:
>>> b = bytes("строка", "cp1251") >>> b.decode(encoding="cp1251"), b.decode("cp1251") ('строка', 'строка')
Для преобразования можно также воспользоваться функцией str():
>>> b = bytes("строка", "cp1251") >>> str(b, "cp1251") 'строка'
Чтобы изменить кодировку данных, следует сначала преобразовать тип bytes в строку, а затем произвести обратное преобразование, указав нужную кодировку. Преобразуем данные из кодировки Windows-1251 в кодировку KOI8-R, а затем обратно:
>>> w = bytes("Строка", "cp1251") # Данные в кодировке windows-1251 >>> k = w.decode("cp1251").encode('koi8-r') >>> k, str(k, 'koi8-r') # Данные в кодировке koi8-r (b'\xf3\xd4\xd2\xcf\xcb\xc1', 'Строка') >>> w = k.decode("koi8-r").encode('cp1251') >>> w, str(w, 'cp1251') # Данные в кодировке windows-1251 (b'\xd1\xf2\xf0\xee\xea\xe0', 'Строка')
На следующем шаге мы рассмотрим тип данных bytearray.