Книга: Марк Лутц - Изучаем Python, 5-е изд., Т. 2
Назад: Проектирование с использованием исключений
Дальше: Управляемые атрибуты

Unicode и байтовые строки

 

До сих пор наше исследование строк в книге было намеренно неполным. При предварительном обзоре типов в главе 4 первого тома были кратко представлены строки и файлы Unicode языка Python без многочисленных деталей, а в главе 7 первого тома, посвященной строкам, их границы были умышленно сужены до подмножества тем о строках, которые необходимо знать большинству программистов на Python.
Так было задумано: поскольку многие программисты, включая большинство новичков, имеют дело с простыми формами текста наподобие ASCII, они могут благополучно работать с базовым строковым типом str в Python и ассоциированными с ним операциями, и не нуждаются в том, чтобы разбираться с более сложными концепциями строк. Фактически такие программисты часто могут игнорировать изменения в строках, внесенные в Python З.Х, и продолжать использовать строки, как они поступали в прошлом.
С другой стороны, многие другие программисты занимаются более специализированными типами данных: наборами символов, отличающимися от ASCII, содержимым файлов изображений и т.д. Для этих программистов и остальных, кто может когда-нибудь к ним присоединиться, мы собираемся здесь дополнить историю со строками Python и взглянуть на ряд более сложных концепций в строковой модели Python.
В частности, мы исследуем основы поддержки Python для текста Unicode — обогащенных символьных строк, применяемых в интернационализированных приложениях, а также двоичных данных — строк, которые представляют абсолютные байтовые значения. Как мы увидим, представление расширенных строк в последних версиях Python разошлось.
• Python З.Х предоставляет для двоичных данных альтернативный строковый тип и в нормальном строковом типе поддерживает текст Unicode (включая ASCII).
• Python 2.Х предоставляет для отличающегося от ASCII текста Unicode альтернативный строковый тип и в нормальном строковом типе поддерживает как простой текст, так и двоичные данные.
Вдобавок поскольку строковая модель Python напрямую влияет на то, как обрабатываются файлы не ASCII, мы также исследуем основы этой связанной темы. Наконец, мы кратко рассмотрим расширенные инструменты для работы со строками и двоичными данными, такие как сопоставление с образцом, обеспечение постоянства объектов посредством модуля pickle, упаковка двоичных данных и разбор XML, а также влияние на них изменений в строках Python З.Х.
Глава официально считается посвященной сложным темам, потому что не всем программистам понадобится погружаться в мир кодировок Unicode или двоичных данных. Одним читателям может хватить предварительного обзора из главы 4 первого тома, другие могут оставить эту главу для чтения в будущем. Однако если у вас когда-либо возникнет необходимость в обработке текста Unicode или двоичных данных, тогда вы обнаружите, что строковая модель Python предлагает нужную поддержку.
Изменения строк в Python З.Х
Одним из наиболее заметных изменений в линейке Python З.Х стала мутация типов строковых объектов. Вкратце типы str и Unicode из Python 2.Х были трансформированы в типы bytes и str в Python З.Х плюс добавился новый изменяемый тип bytearray. Формально тип bytearray доступен также в Python 2.6 и 2.7 (хотя не в более ранних версиях), но он был перенесен из Python З.Х и не настолько ясно проводит различие между текстовым и двоичным содержимым в Python 2.Х.
Такие изменения способны оказать значительное влияние на ваш код, особенно если вы обрабатываете данные, по своей природе имеющие вид Unicode или двоичный. В качестве эмпирического правила следует отметить, что степень важности для вас этой темы во многом зависит от того, под какую из перечисленных ниже категорий вы подпадаете.
• Если вы имеете дело с текстом Unicode, отличным от ASCII — например, в контексте интернационализированных областей вроде веб-сети или результатов, получаемых от некоторых инструментов разбора и баз данных XML и JSON, — тогда вы обнаружите, что поддержка кодировок текста в Python З.Х отличается, но также является, вероятно, более прямой, понятной и бесшовной по сравнению с Python 2.Х.
• Если вы имеете дело с двоичными данными — скажем, в форме файлов с изображениями или аудиоклипами, либо упакованных данных, обрабатываемых с помощью модуля struct, — то вам нужно будет освоить новый объект bytes, введенный в Python З.Х, а также осмыслить другое и более резкое отличие между текстовыми и двоичными данными и файлами в Python З.Х.
• Если вы не относитесь ни к одной из предшествующих двух категорий, тогда обычно можете работать со строками в Python З.Х почти как поступали бы в Python 2.Х, используя универсальный строковый тип str, текстовые файлы и все привычные строковые операции, которые изучались ранее. Интерпретатор Python З.Х будет кодировать и декодировать строки с применением стандартной кодировки вашей платформы (например, ASCII, UTF-8, or Latin-1 — узнать ее можно через вызов locale. getpreferredencoding (False)), но вряд ли вы это заметите.
Другими словами, если вы всегда работаете с текстом ASCII, то можете обойтись нормальными строковыми объектами и текстовыми файлами, пока избегая большей части следующей истории. Как вскоре будет показано, ASCII является простой разновидностью Unicode и подмножеством других кодировок, так что строковые операции и файлы в целом “просто работают”, если ваши программы обрабатывают только текст ASCII.
Тем не менее, даже если вы подпадаете под третью из упомянутых выше категорий, то понимание основ Unicode и строковой модели Python З.Х поможет прояснить лежащее в основе поведение теперь и упростить решение проблем с Unicode или двоичными данными, если они окажут воздействие в будущем.
Выражаясь более строго: нравится это или нет, но Unicode будет частью разработки большинства программного обеспечения в будущем, основы которого мы заложили, и со временем наверняка коснется и вас. Несмотря на то что приложения выходят за рамки материала книги, если вы работаете с веб-сетью, файлами, каталогами, сетевыми интерфейсами, базами данных, каналами, JSON, XML и даже графическими пользовательскими интерфейсами, то Unicode перестает быть необязательной темой в Python З.Х.
Поддержка Python З.Х для Unicode и двоичных данных также доступна в Python
2.Х, хотя и в других формах. Невзирая на то что основное внимание в главе сосредоточено на строковых типах в Python З.Х, для читателей, использующих Python 2.Х, мы попутно исследуем, чем будет отличаться эквивалентная поддержка в Python 2.Х. Безотносительно к применяемой версии Python, описываемые здесь инструменты могут стать важными во многих типах программ.
Основы строк
Прежде чем мы обратимся к коду, давайте начнем с общего обзора строковой модели Python. Чтобы понять, почему в Python З.Х изменился подход в этом направлении, мы должны выяснить, как на самом деле символы представляются в компьютерах — когда они закодированы в файлах и когда хранятся в памяти.
Схемы кодирования символов
Большинство программистов думают о строках как о последовательностях символов, используемых для представления текстовых данных. Несмотря на точность такой формулировки, способ хранения символов может варьироваться в зависимости от того, какой набор символов должен быть записан. Например, когда текст хранится в файлах, применяемый в нем набор символов определяет его формат.
Наборы символов являются стандартами, которые назначают индивидуальным символам целочисленные коды, чтобы они могли быть представлены в памяти компьютера. Скажем, стандарт ASCII был создан в США и формирует понятие текстовых строк для многих программистов из США. Стандарт ASCII определяет коды символов от О до 127 и делает возможным хранение каждого символа в одном 8-битном байте, в котором фактически используются только 7 битов.
Например, стандарт ASCII отображает символ ' а’ на целочисленное значение 97 (шестнадцатеричное 0x61), которое может храниться внутри единственного байта в памяти и файлах. Если вы хотите посмотреть, как это работает, то встроенная функция ord в Python дает двоичное идентифицирующее значение для символа, a chr возвращает символ для заданного значения целочисленного кода:
>>> ord('a') # 'a ' - байт, закодированный как значение 97
# в стандарте ASCII (и других)
97
»> hex(97)
'0x61’
>>> chr(97) # В ASCII значение целочисленного кода 91 обозначает символ 'а'
1 а'
Однако иногда одного байта на символ недостаточно. Скажем, разнообразные символы и диакритические знаки не умещаются в диапазон возможных символов, определяемых ASCII. Чтобы приспособиться к специальным символам, некоторые стандарты для представления символов применяют все допустимые значения в 8-битном байте, 0-255, и назначают специальным символам значения от 128 до 255 (за пределами диапазона ASCII).
Один такой стандарт, известный как набор символов Latin-1, широко используется в Западной Европе. В стандарте Latin-1 коды символов выше 127 назначаются диакритическим знакам и специальным символам. Например, байтовое значение 196 соответствует специально помеченному символу, не входящему в набор ASCII:
»> 0хС4
196
>>> chr(196) # Показана результирующая форма в Python З.Х
'А'
Стандарт Latin-1 делает возможным представление обширного комплекта дополнительных специальных символов, но по-прежнему поддерживает ASCII как 7-битное подмножество своей 8-битной реализации.
Тем не менее, в ряде алфавитов определено настолько много символов, что представить каждый из них как один байт попросту невозможно. Unicode обеспечивает более высокую гибкость. Временами на текст Unicode ссылаются как на строки “широких символов”, потому что при необходимости символы могут быть представлены посредством множества байтов. Unicode обычно применяется в интернационализированных программах для представления европейских, азиатских и других неанглийских наборов символов, которые имеют больше символов, чем способны представить 8-битные байты.
Чтобы хранить обогащенный текст подобного рода в памяти компьютера, такие символы нужно транслировать в низкоуровневые байты с использованием кодировки. Кодировка определяет правила для перевода строки символов Unicode в последовательность байтов и извлечения строки из последовательности байтов. Более формально трансляция туда и обратно между байтами и строками определяется двумя терминами:
• кодирование — это процесс перевода строки символов в форму низкоуровневых байтов согласно заданному имени кодировки;
• декодирование — это процесс перевода низкоуровневой строки байтов в форму строки символов согласно ее имени кодировки.
То есть мы кодируем строку в низкоуровневые байты и декодируем низкоуровневые байты в строку. Для сценариев декодированные строки являются просто символами в памяти, но могут быть закодированы в разнообразные представления байтовых строк при хранении в файлах, передаче по сетям, внедрении в документы и базы данных и т.д.
В некоторых кодировках процесс трансляции тривиален — скажем, ASCII и Latin-1 отображают каждый символ на одиночный байт фиксированного размера, так что никакой работы по переводу не требуется. Для других кодировок отображение может быть более сложным и выдавать множество байтов на символ даже в случае простых 8-битных форм текста.
Например, широко применяемая кодировка UTF-8 позволяет представлять большой диапазон символов за счет использования схемы с переменным количеством байтов. Символы с кодами ниже 128 представляются как один байт; символы с кодами между 128 и Ox7ff (2047) переводятся в 2 байта, где каждый байт имеет значение 128— 255, а символы с кодами выше 0x7ff переводятся в 3- или 4-байтовые последовательности со значениями байтов 128-255. Это позволяет сохранять простые строки ASCII компактными, обходить вопросы упорядочения байтов и избегать нулевых байтов, которые могут вызывать проблемы при работе с библиотеками С и сетями.
Поскольку кодировки Latin-1 и UTF-8 ради совместимости отображают назначенные символы на те же самые коды, ASCII является подмножеством указанных кодировок. То есть допустимая строка символов ASCII также будет допустимой строкой, закодированной с помощью Latin-1 и UTF-8. Скажем, каждый файл ASCII считается допустимым файлом UTF-8, т.к. набор символов ASCII представляет собой 7-битное подмножество UTF-8.
И наоборот, кодировка UTF-8 совместима в двоичном виде с ASCII, но только для кодов символов меньше 128. Кодировки Latin-1 и UTF-8 просто дают возможность представлять дополнительные символы: Latin-1 — символы, отображаемые на значения от 128 до 255 внутри байта, a UTF-8 — символы, которые могут быть представлены посредством множества байтов.
Другие кодировки позволяют представлять более широкие наборы символов различными способами. Например, UTF-16 и UTF-32 форматируют текст со схемой соответственно 2 и 4 байта фиксированного размера на каждый символ даже для символов, которые иначе уместились бы в один байт. Некоторые кодировки могут также вставлять префиксы, идентифицирующие порядок следования байтов.
Чтобы увидеть все самостоятельно, вызовите метод encode строки, который выдает ее в формате байтовой строки, закодированной в соответствии с указанной схемой — 2-символьная строка ASCII занимает 2 байта в ASCII, Latin-1 и UTF-8, но она намного шире в UTF-16 и UTF-32, к тому же включает заголовочные байты:
»> S * 'ni'
»> S.encode('ascii'), S.encode(1latinl1), S.encode(1utf81)
(b’ni', b'ni1, b' ni ' )
»> S.encode('utf 16') , len (S.encode ('utf 16'))
(b1\xff\xfen\x00i\x00', 6)
»> S .encode (’utf32') , len (S.encode ('utf32'))
(b'\xff\xfe\x00\x00n\x00\x00\x00i\x00\x00\x00', 12)
В Python 2.X результаты слегка отличаются (вы не получите ведущий символ b для байтовых строк). Но все эти схемы кодирования — ASCII, Latin-1, UTF-8 и многие другие — считаются Unicode.
При программировании на Python кодировки указываются как строки, содержащие имя кодировки. Python поступает с приблизительно сотней разных кодировок; полный список ищите в справочном руководстве по библиотеке Python. Импортирование модуля encodings и выполнение help (encodings) также покажет многие имена кодировок; одни реализованы в Python, другие в С. Кроме того, некоторые кодировки имеют несколько имен; например, latin-l, iso_8859_l и 8859 — синонимы кодировки Latin-1. Мы еще вернемся к кодировкам позже в главе, когда будем исследовать методики написания строк Unicode в сценарии.
За дополнительными сведениями о кодировке Unicode обращайтесь в стандартный набор руководств по Python. Он содержит подраздел “Unicode HOWTO” внутри раздела “Python HOWTOs”, где вы найдете информацию, которая ради экономии места здесь было опущена.
Хранение строк Python в памяти
Кодировки из предыдущего раздела на самом деле применяются, только когда текст хранится или передается внешне, в файлах или на других носителях. В памяти Python всегда хранит декодированные строки текста в нейтральном к кодировкам формате, который может использовать или не использовать множество байтов для каждого символа. Вся обработка текста происходит в таком унифицированном внутреннем формате. Текст транслируется в специфичный для кодировки формат и обратно только при передаче во внешние текстовые файлы, байтовые строки либо API-интерфейсы с особыми требованиями к кодировке либо из них. Они являются просто строковыми объектами, представленными в настоящей книге.
Хотя приведенная далее информация не имеет отношения к коду, она может помочь некоторым читателям лучше осознать происходящее. Способ действительного хранения текста в памяти подвержен изменениям с течением времени и фактически он претерпел значительную мутацию, начиная с версии Python 3.3.
Python 3.2 и более ранние версии
Вплоть до версии Python 3.2 строки хранились внутренне в формате с фиксированной длиной UTF-16 (грубо говоря, UCS-2) с 2 байтами на символ, если только Python не был сконфигурирован на выделение 4 байтов на символ (UCS-4).
Python 3.3 и более поздние версии
В Python 3.3 и последующих версиях взамен применяется схема с переменной длиной с 1, 2 или 4 байтами на символ в зависимости от содержимого строки. Размер выбирается на основе символа с наибольшим порядковым значением Unicode в представленной строке. Такая схема делает возможным эффективное в отношении пространства представление для распространенных случаев, но также позволяет использовать полный формат UCS-4 на всех платформах.
Новая схема, появившаяся в Python 3.3, является оптимизацией, особенно в сравнении с прошлыми широкими формами Unicode. Согласно документации по Python, объем занимаемой памяти делится на 2-4 в зависимости от текста; кодирование строки ASCII в UTF-8 больше не нуждается в перекодировании символов, потому что ее представления ASCII и UTF-8 одинаковы; повторение одиночной буквы ASCII и получение подстроки из строки ASCII выполняется в 4 раза быстрее; UTF-8 оказывается в 2-4 раза быстрее; и кодирование UTF-16 — до 10 раз быстрее. Некоторые эталонные тесты показывают, что общее потребление памяти в Python 3.3 стало в 2-3 раза ниже, чем в Python 3.2, и похоже на менее ориентированную на Unicode версию Python 2.7.
Независимо от используемой схемы хранения, как отмечалось в главе 6 первого тома, Unicode очевидно требует от нас думать о строках в терминах символов, а не байтов. Это может стать более крупным препятствием для программистов, привыкших к более простому миру с одной лишь кодировкой ASCII, где каждый символ отображается на одиночный байт, но такая идея больше неприменима с точки зрения как результатов вызова инструментов для работы с текстовыми строками, так и физического размера символов.
Инструменты обработки текста
В наши дни содержимое и длина строки в действительности выражаются в кодовых точках Unicode — идентифицирующих порядковых числах для символов. Например, встроенная функция ord теперь возвращает порядковое значение кодовой точки Unicode символа, которое не обязательно будет кодом ASCII и может умещаться или не умещаться в один 8-битный байт. Аналогично len возвращает количество символов, не байтов; строка, вероятно, занимает больше места в памяти, а ее символы могут не умещаться в байты.
Размер текста
Как демонстрировалось в примерах главы 4 первого тома, одиночный символ в Unicode не обязательно отображается напрямую на одиночный байт, либо при кодировании в файле, либо при хранении в памяти. Даже символы простого текста в 7-битном ASCII могут не отображаться на байты — UTF-16 использует множество байтов на символ в файлах, a Python может выделять 1, 2 или 4 байта на символ в памяти. Мышление в терминах символов позволяет нам абстрагироваться от деталей внешнего и внутреннего хранения.
Однако основной момент здесь в том, что кодирование имеет отношение главным образом к файлам и передаче. После загрузки в строку Python с текстом в памяти не связано понятие “кодировка” и он является просто последовательностью символов Unicode (известных как кодовые точки), хранящихся обобщенным образом. В сценарии обращение к такой строке осуществляется как к строковому объекту Python — тема следующего раздела.
Типы строк Python
На более конкретном уровне язык Python предлагает строковые типы данных для представления символьного текста в сценариях. Применяемые в сценариях строковые типы зависят от используемой версии Python. В Python 2.Х имеется универсальный строковый тип для представления двоичных данных и простого 8-битного текста вроде ASCII, а также специфический тип для представления обогащенного текста Unicode:
• str для представления 8-битного текста и двоичных данных;
• Unicode для представления декодированного текста Unicode.
Два строковых типа Python 2.Х отличаются (unicode разрешает некоторым символам Unicode иметь добавочный размер, также обладает дополнительной поддержкой для кодирования и декодирования), но их наборы операций значительно перекрываются. Строковый тип str в Python 2.Х применяется для текста, который может быть представлен с помощью 8-битных байтов (включая ASCII и Latin-1), равно как двоичных данных, представляющих абсолютные байтовые значения.
По контрасту с этим Python З.Х поступает с тремя типами строковых объектов — один для текстовых данных и два для двоичных данных:
• str для представления декодированного текста Unicode (в том числе ASCII);
• bytes для представления двоичных данных (включая декодированный текст);
• bytearray, изменяемая разновидность типа bytes.
Как упоминалось ранее, тип bytearray также доступен в Python 2.6 и 2.7, но он просто перенесен из Python З.Х, имеет менее специфичное к содержимому поведение и в целом считается типом Python З.Х.
Для чего нужны разные строковые типы?
Все три строковых типа в Python З.Х поддерживают похожие наборы операций, но они исполняют разные роли. Главной целью такого изменения в Python З.Х было объединение нормальных строковых типов и строковых типов Unicode, определяемых Python 2.Х, в единственный строковый тип, который поддерживает простой текст и текст Unicode: разработчики хотели устранить противопоставление между строками Python 2.Х и сделать обработку Unicode более естественной. С учетом того, что ASCII и другой 8-битный текст на самом деле являются простым видом Unicode, подобное сближение выглядело вполне логичным.
Для достижения этого в Python З.Х текст хранится в заново определенном типе str — неизменяемой последовательности символов (не обязательно байтов). Последовательность может содержать либо простой текст, такой как ASCII со значениями символов, умещающимися в одиночный байт, либо обогащенный текст наподобие UTF-8 со значениями символов, которые могут требовать нескольких байтов. Строки, обрабатываемые сценарием посредством типа str, хранятся в памяти обобщенным образом и кодируются в байтовые строки и декодируются из них согласно либо стандартной для платформы кодировке Unicode, либо явно указанному имени кодировки. В итоге сценарии получают возможность транслировать текст в различные кодировки, как для сохранения в памяти, так и при взаимодействии с файлами.
Хотя новый тип str в Python З.Х добивается желательного объединения строк/ Unicode, многим программам по-прежнему необходимо обрабатывать низкоуровневые двоичные данные, которые не закодированы в каком-либо текстовом формате. К такой категории относятся файлы с изображениями и аудиоклипами плюс упакованные данные, используемые ддя. взаимодействия с устройствами или программами С, которые вы можете обрабатывать с помощью модуля struct библиотеки Python. Поскольку строки Unicode декодируются из байтов, они не могут применяться для представления байтов.
Для поддержки обработки таких по-настоящему двоичных данных также был введен новый строковый тип bytes — неизменяемая последовательность 8-битных целых чисел,., представляющих абсолютные байтовые значения, которые по возможности выводятся как символы ASCII. Несмотря на то что bytes является отдельным типом, он поддерживает почти все те же операции, что и тип str; сюда входят строковые методы, операции над последовательностями и даже сопоставление с образцом из модуля ге, но не строковое форматирование. В Python 2.Х эту роль для двоичных данных удовлетворяет универсальный тип str, т.к. его строки являются всего лишь последовательностями байтов; отдельные тип Unicode обрабатывает обогащенные текстовые строки.
Обратимся к деталям: объект bytes в Python З.Х на самом деле представляет собой последовательность коротких целых чисел, каждое из которых находится в диапазоне 0-255; индексирование bytes возвращает int, нарезание bytes возвращает еще один объект bytes, а выполнение встроенной функции list на bytes возвращает список целых чисел, не символов. Тем не менее, при обработке посредством операций, которые рассчитывают на символы, содержимое объектов bytes трактуется как байты, закодированные посредством ASCII (например, метод isalpha предполагает, что каждый байт является кодом символа ASCII). Более того, для удобства объекты bytes выводятся как строки символов, а не целые числа.
Работая над изменением строковых типов в Python З.Х, разработчики также добавили тип bytearray, который является изменяемым вариантом типа bytes и потому поддерживающим изменения на месте. Подобно типам str и bytes он поддерживает обычные строковые операции и многие операции изменения на месте, характерные для списков (например, методы append и extend, а также присваивание по индексу). Тип bytearray может быть удобен при работе с действительными двоичными данными и простыми видами текста. Исходя из предположения, что ваши текстовые строки могут трактоваться как низкоуровневые 8-битные байты (скажем, текст ASCII или Latin-1), тип bytearray в заключение добавляет прямую изменяемость на месте для текстовых данных, которая временами невозможна без преобразования в изменяемый тип в Python 2.Х и поддерживается типом str или bytes в Python З.Х.
Несмотря на то что Python 2.Х и Python З.Х предлагают почти ту же самую функциональность, они упаковывают ее по-разному. Строковые типы Python 2.Х отображаются на строковые типы Python З.Х не полностью прямо. Дело в том, что тип str из Python 2.Х приравнивается к типам str и bytes в Python З.Х, а тип str из Python З.Х приравнивается к типам str и Unicode в Python 2.Х. Кроме того, изменяемость типа bytearray в Python З.Х уникальна.
Однако по существу эта асимметрия не так страшна, как может показаться. Она сводится к следующему: в Python 2.Х вы будете использовать тип str для простых текстовых и двоичных данных и тип Unicode для расширенных форм текста, чьи наборы символов не отображаются на 8-битные байты; в Python З.Х вы будете применять str для любой разновидности текста (ASCII, Latin-1 и все остальные виды Unicode) и bytes или bytearray для двоичных данных. На практике выбор часто делается за вас используемыми инструментами — особенно в случае инструментов обработки файлов, которые рассматриваются ниже.
Текстовые и двоичные файлы
Файловый ввод-вывод в Python З.Х также был модернизирован, чтобы отражать разграничение str/bytes и автоматически поддерживать кодирование текста Unicode при передачах. Теперь в Python З.Х проведено четкое и независимое от платформы различие между текстовыми и двоичными файлами.
Текстовые файлы
Когда файл открывается в текстовом режиме, чтение его данных автоматически декодирует его содержимое и возвращает его как объект str; запись берет объект str и автоматически кодирует его перед передачей в файл. Операции чтения и записи выполняют трансляцию в соответствии со стандартной кодировкой для платформы или указанным именем кодировки. В текстовом режиме файлы также поддерживают универсальный перевод признака конца файла и дополнительные аргументы спецификации кодировки. В зависимости от имени кодировки текстовые файлы могут также автоматически обрабатывать последовательность в начале файла, обозначающую порядок следования байтов (рассматривается далее в главе).
Двоичные файлы
Когда файл открывается в двоичном режиме за счет добавления b (только в нижнем регистре) к аргументу строки режима во встроенном вызове open, чтение его данных их не декодирует, а возвращает в низкоуровневом и неизмененном виде как объект bytes; подобным же образом запись берет объект bytes и передает его в файл без изменений. В двоичном режиме файлы также принимают объект bytearray для содержимого, подлежащего записи в файл.
Поскольку язык проводит четко разграничение между str и bytes, вы должны решить, являются ли ваши данные текстовыми или двоичными по своей природе, и применять объекты типа либо str, либо bytes для надлежащего представления содержимого в сценарии. В конечном итоге режим, в котором вы открываете файл, будет диктовать тип объекта, используемый в сценарии для представления его содержимого.
• Если вы обрабатываете файлы изображений, данные, передаваемые по сети, упакованные двоичные данные, содержимое которых должно быть извлечено, или потоки данных из устройств, тогда велики шансы, что вы захотите применять тип bytes и файлы в двоичном режиме. Вы также можете выбрать тип bytearray, если пожелаете обновлять данные, не создавая их копии в памяти.
• Если взамен вы обрабатываете что-то текстовое по своей природе, такое как вывод программы, HTML-разметка, содержимое сообщения электронной почты либо файлы CSV или XML, тогда вероятно захотите использовать тип str и файлы в текстовом режиме.
Обратите внимание, что аргумент строки режима для встроенной функции open (второй аргумент) в Python З.Х становится довольно важным — его содержимое не только задает режим обработки файла, но также подразумевает объектный тип Python. Добавляя b в строку режима, вы указываете двоичный режим и будете получать или обязаны предоставлять объект bytes для представления содержимого файла при чтении или записи. В случае отсутствия b файл обрабатывается в текстовом режиме, и для представления его содержимого в сценарии вы будете применять объекты str. Например, режимы rb, wb и rb+ подразумевают тип bytes, а г, w+ и rt (по умолчанию) — тип str.
Файлы в текстовом режиме также обрабатывают последовательность с маркером порядка следования байтов (byte order mark — BOM), которая может появляться в начале файлов в условиях ряда схем кодирования. Скажем, в кодировках UTF-16 и UTF-32 маркер ВОМ указывает формат с обратным или прямым порядком байтов (по существу, какой из концов битовой строки более значащий) — в качестве примеров просмотрите ведущие байты в результатах вызовов кодирования UTF-16 и UTF-32 ранее в главе. Текстовый файл UTF-8 также может включать маркер ВОМ для объявления о том, что в целом он относится к UTF-8. При чтении и записи данных с использованием этих схем кодирования интерпретатор Python пропускает или записывает маркер ВОМ в соответствии с правилами, которые приводятся позже в главе.
В Python 2.Х поддерживает то же самое поведение, но для доступа к данным, основанным на байтах, применяются нормальные файлы, которые открываются посредством open, а для обработки текстовых данных Unicode используются файлы Unicode, открываемые с помощью вызова codecs. open. Как мы увидим далее в главе, во втором случае при передаче также выполняется кодирование и декодирование. Для начала мы займемся исследованием строковой модели Unicode в Python.
Написание базовых строк
Давайте рассмотрим несколько примеров, которые продемонстрируют применение строковых типов Python З.Х. Одно предварительное замечание: код в текущем разделе выполняется только под управлением Python З.Х. Тем не менее, базовые строковые операции большей частью переносимы между версиями Python. Простые строки ASCII, представляемые посредством типа str, в Python 2.Х и Python З.Х работают одинаково (и в точности, как было описано в главе 7 первого тома).
Более того, хотя тип bytes в Python 2.Х отсутствует (есть лишь универсальный тип str), под управлением Python 2.Х обычно можно запускать код, где предполагается его наличие. В версиях Python 2.6 и 2.7 вызов bytes (X) представляет собой синоним str (X), а новая литеральная форма b' . . . ’ считается той же самой, что и нормальный строковый литерал Однако в отдельных случаях вы все еще можете столкнуться с нестыковкой версий; например, вызов bytes в Python 2.6/2.7 не требует и не разрешает передавать второй аргумент (имя кодировки), который обязателен в bytes из Python З.Х.
Строковые литералы Python З.Х
Строковые объекты Python З.Х порождаются, когда вы вызываете встроенную функцию, такую как str или bytes, читаете файл, созданный вызовом open (описан в следующем разделе), либо используете литеральный синтаксис в своем сценарии. В Python З.Х для создания объектов bytes применяется новая литеральная форма b' ххх' (и эквивалентная ей В1 ххх'), а объекты bytearray можно создавать вызовом функции bytearray с различными аргументами.
Выражаясь более формально, в Python З.Х все текущие формы строковых литералов — 1 ххх1, "ххх" и блоки в утроенных кавычках — генерируют объект str; добавление b или В перед любой из них приводит к созданию объекта bytes. Новый байтовый литерал b ’ . . . ’ по своей форме похож на низкоуровневую строку г1 ... 1, используемую для того, чтобы избежать отмены специального значения символов обратной косой черты. Взгляните на следующее взаимодействие в Python З.Х:
С:\code> С:\python37\python
>>> В * b'spam' # Байтовый литерал Python З.Х создает объект bytes
# (8-битовые байты)
»> S ■ 'eggs' # Строковый литерал Python З.Х создает объект
# текстовой строки Unicode
»> type (В) , type (S)
(<class 'bytes’>, cclass ’str'>)
>>> В # bytes: последовательность целых чисел,
# выводится как строка символов
b'spam’
>>> S 'eggs 1
Объект bytes в Python З.Х в действительности является последовательностью коротких целых чисел, хотя он выводит свое содержимое в виде символов всякий раз, когда это возможно:
>>> B[0],S[0] # Индексирование возвращает целое число
# для bytes и строку для str
(115, 'е')
>>> B[1:],S[1:] # Нарезание создает еще один объект bytes или str
(b'pam1, 1ggs1)
>» list(B) , list(S)
( [115, 112, 97, 109] , [' e ', ’ g' , ' g', ' s ' ] ) § На самом деле bytes -
# 8-битовые короткие целые числа
Объект bytes также неизменяем почти как str (но описываемый позже bytearray — нет); присваивать по смещению bytes объект str, bytes или целого числа нельзя:
>>> В[0] = 'х1 # Оба неизменяемы
TypeError: ’bytes' object does not support item assignment
Ошибка типа: объект bytes не поддерживает присваивание в отношении элементов »> S[0] = 'х'
TypeError: 'str' object does not support item assignment
Ошибка типа: объект str не поддерживает присваивание в отношении элементов
Наконец, обратите внимание на то, что префикс b или В литерала bytes также работает с любой формой строкового литерала, включая блоки в утроенных кавычках, несмотря на то, что вы получаете строку низкоуровневых байтов, которые могут отображаться или не отображаться на символы:
>>> # Префикс литерала bytes работает с одинарными, двойными,
»> # утроенными кавычками и низкоуровневыми байтами >» В = в1'"”
. . . хххх
• • • УУУУ
к н п
>>> В
b'\nxxxx\nyyyy\n'
Литералы Unicode из Python 2.Х, начиная с версии Python 3.3
Формы строковых литералов Unicode из Python 2.Х вида и1 ххх1 и U'xxx1 были удалены в версии Python 3.0, потому что их сочли избыточными — нормальные строки Python З.Х представлены в кодировке Unicode. Тем не менее, для содействия прямой и обратной совместимости они снова стали доступными в версии Python 3.3, где трактуются как нормальные строки str:
С:\code> С:\python37\python
>>> U = и'spam' # Литерал Unicode из Python 2.Х воспринимается в Python 3.3+ у>> type(U) # Он является просто строкой str, но обратно совместим
<class 'str’>
>>> U 'spam'
»> U[0]
' s'
»> list(U)
['s', 'p1, ’a', 'm']
Эти литералы отсутствовали в версиях Python 3.0-3.2, где взамен нужно было применять 1 ххх'. В целом вы должны использовать текстовые литералы * ххх1 из Python
З.Х в новом коде, ориентированном только на Python З.Х, поскольку форма из Python
2.Х избыточна. Однако в Python 3.3 и последующих версиях применение литеральной формы из Python 2.Х может облегчить задачу переноса кода Python 2.Х и повысить совместимость кода Python 2.Х (просмотрите пример с форматированием денежных значений, который был приведен в главе 25 первого тома и упоминается ниже). Тем не менее, безотносительно к тому, как текстовые строки записываются в Python З.Х, все они представлены в Unicode, даже если содержат только символы ASCII (см. раздел “Написание текста, отличающегося от ASCII” далее в главе).
Строковые литералы Python 2.Х
Все три формы строковых литералов Python З.Х, приведенные в предыдущем разделе, могут быть записаны в Python 2.Х, но их смысл отличается. Ранее упоминалось, что в версиях Python 2.6 и 2.7 байтовый литерал b' ххх1 присутствует для прямой совместимости с Python З.Х, но он такой же, как 1 ххх', и создает объект str (b игнорируется), a bytes представляет собой всего лишь синоним для str. Вы уже видели, что в Python З.Х оба они относятся к отдельному типу bytes:
С:\code> С:\python27\python
»> В = b'spam1 # Байтовый литерал Python З.Х - это просто str в Python 2. 6/2. 7 >>> S = 'eggs' # str является последовательностью байтов/символов
>» type (В), type(S)
(<type 'str’>, ctype 'str’>)
»> В, S
('spam', 'eggs')
»> B[0] , S[0]
( 's' , 'e' )•
>>> list(B), list(S)
(['s', 'p\ 'a', 'm' ] , [' e', 'g', 'g\ -s'])
В Python 2.X специальный литерал и тип Unicode умещают в себе обогащенные формы текста:
>>> U = u'spam' # Литерал Unicode из Python 2.Х создает отдельный тип »> type(U) # Работает также, начиная с Python 3.3, но является там просто str ctype 'Unicode'>
>» и
и'spam'
»> U[0] и' s'
»> list (U)
[и's', u'p', u'a', u'm']
Как мы видели, для совместимости такая форма работает также в Python 3.3 и последующих версиях, но просто создает там нормальный объект str (и игнорируется).
Преобразования строковых типов
Несмотря на то что в Python 2.Х объекты типов str и Unicode разрешено смешивать в выражениях (когда объект str содержит только 7-битный текст ASCII), в Python
З.Х между ними проводится более четкое различие — объекты типов str и bytes никогда автоматически не смешиваются в выражениях и никогда автоматически не преобразуются друг в друга при передаче в функции. Функция, которая ожидает в аргументе объект str, обычно не будет принимать объект bytes и наоборот. По указанной причине Python З.Х по существу требует, чтобы вы придерживались одного типа или другого либо при необходимости выполняли ручные явные преобразования:
• str .encode () и bytes (S, encoding) транслируют строку в форму с низкоуровневыми байтами и в процессе создают закодированный объект bytes из декодированного объекта str;
• bytes .decode () и str (В, encoding) транслируют низкоуровневые байты в форму строки и в процессе создают декодированный объект str из закодированного объекта bytes.
Оба метода, encode и decode, а также вызовы open, которые нам предстоит рассмотреть, используют либо явно передаваемое имя кодировки, либо принятое по умолчанию. В Python З.Х стандартной кодировкой для методов всегда будет UTF-8, но open применяет значение из модуля locale, которое может варьироваться в зависимости от платформы. В Python 2.Х стандартной кодировкой в обоих случаях обычно является ASCII, как отражено в модуле sys (что допускает изменения при начальном запуске). Вот пример для Python З.Х:
>>> S = 'eggs'
>>> S.encode() # str->bytes: закодированный текст в низкоуровневые байты b1eggs'
>>>bytes(S, encoding=’ascii') # str->bytes, альтернатива b'eggs'
>>> В = b' spam'
>» B.decode () # bytes->str: декодированные низкоуровневые байты в текст
'spam'
>>> str(В, encoding='ascii') # bytes->str, альтернатива
1 spam'
Есть два предостережения. Во-первых, разнообразные стандартные кодировки вашей платформы доступны в модулях sys и locale, но аргумент кодировки в bytes обязателен, несмотря на необязательность в str. encode (и bytes .decode).
Во-вторых, хотя вызовы str не требуют аргумента кодировки, как делает bytes, его отсутствие в вызовах str вовсе не означает, что будет использовать стандартная кодировка — вызов str без аргумента кодировки возвращает выводимую строку объекта bytes, а не его преобразованную в str форму (обычно это не то, что вас будет интересовать!). Предположим, что В и S остались в том же виде, как в предыдущем взаимодействии:
»> import sys, locale # ореп() в Windows использует ср1252
# (подмножество Latin-1)
»> sys.platform # Но str() никогда не использует стандартную кодировку...
1 Win32'
>>> locale.getpreferredencoding(False), sys.getdefaultencoding()
('cpl252', 'utf-8 ' )
»> bytes (S)
TypeError: string argument without an encoding TypeError: строковый аргумент без кодировки
»> str (В) # str без кодировки
"b'spam'" # Выводимая строка, не преобразование!
»> len (str (В))
7
»> len(str(В, encoding='ascii')) # Используйте кодировку для
# преобразования в форму str
4
При наличии сомнений передавайте в Python З.Х аргумент с именем кодировки, даже если преобразование может иметь стандартный вариант. В Python 2.Х преобразования похожи, хотя поддержка смешивания строковых типов в выражениях Python
2.Х делает преобразования необязательными для текста ASCII и для другой модели строковых типов названия инструментов отличаются. В Python 2.Х преобразования происходят между закодированным объектом str и декодированным объектом
Unicode, а не так, как в Python З.Х — между закодированным объектом bytes и декодированным объектом str:
»> S, U = 'spam' , u'eggs' # Инструменты преобразования строковых типов Python 2.Х »> S, U
(1 spam', и'eggs')
»> Unicode(S) , str(U) # Python 2.X преобразует str->unicode, unicode->str (u'spam', 'eggs')
»> S.decode() , U.encode() # в сравнении с bytes->str, str->bytes в Python З.Х (и'spam', 'eggs')
Написание строк Unicode
Кодирование и декодирование станут более значимыми, когда вы начнете иметь дело с текстом Unicode, отличающимся от ASCII. Для записи произвольных в строках символов Unicode, часть которых может даже не удастся набрать на клавиатуре, строковые литералы Python поддерживают шестнадцатеричные байтовые управляющие последовательности M\xNN" и управляющие последовательности Unicode вида "\uNNNN" и " \UNNNNNNNN". В управляющих последовательностях Unicode первая форма дает четыре шестнадцатеричные цифры для кодирования 2-байтовой (16-битной) кодовой точки символа, а вторая — восемь цифр для 4-байтовой (32-битной) кодовой точки. Байтовые строки поддерживают только шестнадцатеричные управляющие последовательности для закодированного текста и другие формы для данных, основанных на байтах.
Написание текста ASCII
Давайте проработаем несколько примеров, которые продемонстрируют основы написания текста. Как было показано, текст ASCII является простым типом Unicode, хранящимся в виде последовательности байтовых значений, которые представляют символы:
C:\code> C:\python37\python
>>> ord('X’) # 'X' имеет двоичное значение кодовой точки 88
# в стандартной кодировке
88
>>> chr(88) # 88 обозначает символ 'Хг
'X'
>» S = 'XYZ’ # Строка Unicode текста ASCII
»> S
'XYZ'
>» len(S) # Длина в три символа
3
>>> [ord(c) for с in S] # Три символа с целочисленными порядковыми значениями [88, 8 9, 90]
Нормальный 7-битный текст ASCII такого рода представлен с помощью одного символа на байт в каждой схеме кодирования Unicode, описанной ранее в главе:
»> S.encode('ascii’) # Значения 0..127 в 1 байте (1 бит) каждое b'XYZ'
>>> S.encode('latin-1') # Значения 0..255 в 1 байте (8 бит) каждое b'XYZ'
>>> S.encode(’utf-8') # Значения 0. .127 в 1 байте, 128..2047 в 2 байтах,
# остальные в 3 или 4 байтах
b'XYZ'
Фактически объекты bytes, возвращаемые за счет кодирования текста ASCII подобным способом, представляют собой последовательность коротких целых чисел, которые по возможности выводятся как символы ASCII:
>>> S.encode(1latin-1')
b'XYZ'
»> S.encode('latin-1') [0]
88
>>> list(S.encode('latin-1'))
[88, 89, 90]
Написание текста, отличающегося от ASCII
Формально для написания отличных от ASCII символов мы можем применять:
• шестнадцатеричные управляющие последовательности или управляющие последовательности Unicode для внедрения порядковых значений кодовых точек в текстовые строки — нормальные строковые литералы в Python З.Х и строковые литералы Unicode в Python 2.Х (а также в Python 3.3 и последующих версиях для совместимости);
• шестнадцатеричные управляющие последовательности для внедрения закодированного представления символов в байтовые строки — нормальные строковые литералы в Python 2.Х и литералы в виде байтовых строк в Python З.Х (а также в Python 2.Х для совместимости).
Обратите внимание, что текстовые строки содержат действительные значения кодовых точек, тогда как байтовые строки содержат их закодированную форму Значение закодированного представления символа в байтовой строке будет таким же, как и значение его декодированной кодовой точки Unicode в текстовой строке, только для определенных символов и кодировок. В любом случае шестнадцатеричные управляющие последовательности ограничены указанием однобайтовых значений, но с помощью управляющих последовательностей Unicode можно указывать символы со значениями шириной 2 и 4 байта. Функция chr также может использоваться для создания одиночного символа не ASCII из значения кодовой точки и, как мы увидим позже, объявления в исходном коде применяются к таким символам, внедренным в сценарий.
Например, шестнадцатеричные значения 0хС4 и 0хЕ8 являются кодами для двух специальных диакритических знаков за пределами 7-битного диапазона ASCII, но мы можем внедрять их в объекты str в Python З.Х, потому что тип str поддерживает Unicode:
>>> chr(0xc4) # 0хС4, 0хЕ8: символы за пределами диапазона ASCII
'А'
>>> chr(0xe8)
'ё'
»> S = '\xc4\xe8'
# 8-битные шестнадцатеричные управляющие
# последовательности: две цифры
»> S
' Аё'
»> S = '\u00c4\u00e81 # 16-битные управляющие последовательности
# Unicode: четыре цифры каждое
»> S
' Аё'
»> len(S) # Длина в два символа (не количество байтов!)
2
Обратите внимание, что в строковых литералах с текстом Unicode вроде показанных выше шестнадцатеричные управляющие последовательности и управляющие последовательности Unicode указывают значение кодовой точки Unicode, но не байтовое значение. Шестнадцатеричная управляющая последовательность х требует в точности две цифры (для 8-битных значений кодовых точек), а управляющая последовательность Unicode с и и U — соответственно четыре и восемь шестнадцатеричных цифр для указания значений кодовых точек, которые могут достигать 16 и 32 битов:
>>> S = '\U000000c4\U000000e8' # 32-битные управляющие последовательности
# Unicode: восемь цифр каждое
»> S ’Аё’
Как будет показано позже, в данном отношении Python 2.Х работает аналогично, но управляющие последовательности Unicode разрешены только в литеральной форме Unicode. Они работают здесь в нормальных строковых литералах Python З.Х лишь потому, что нормальные строки в этой линейке всегда представлены в кодировке Unicode.
Кодирование и декодирование текста, отличающегося от ASCII
Если теперь мы попытаемся закодировать строку с текстом, отличающимся от ASCII, из предыдущего раздела в низкоуровневые байты как ASCII, то получим ошибку, потому что ее символы выходят за пределы диапазона 7-битных значений кодовых точек ASCII:
»> S = ' \u00c4\u00e8' # Строка с текстом не ASCII длиной в два символа
»> S
'Аё'
>» len(S)
2
»> S. encode (' ascii')
UnicodeEncodeError: 'ascii' codec can't encode characters in position 0-1: ordinal not in range(128)
Ошибка кодирования Unicode: кодек ascii не может закодировать символы в позициях 0-1: порядковое значение не в диапазоне range (128)
Однако кодирование такой текстовой строки как Latin-1 работает, поскольку каждый символ попадает в 8-битный диапазон данной кодировки, и мы получаем 1 байт на символ, выделенный в закодированной байтовой строке. Кодирование как UTF-8 тоже работает: эта кодировка поддерживает широкий диапазон кодовых точек Unicode, но взамен выделяет 2 байта на символ, отличный от ASCII. Если такие закодированные строки записываются в файл, тогда показанные здесь в качестве результатов кодирования низкоуровневые объекты bytes и будут тем, что фактически сохраняется в файле для указанных типов кодировок:
>» S.encode(' latin-1') # 1 байт на символ при кодировании
b'\xc4\xe8'
>>> S.encode('utf-8') # 2 байта на символ при кодировании
b'\xc3\x84\xc3\xa8'
>» len(S.encode ('latin-1')) # 2 байта в latin-1, 4 байта в utf-8
2
>» len (S. encode (1 utf-8'))
4
Обратите внимание, что вы также можете пойти другим путем, читая низкоуровневые байты из файла и декодируя их обратно в строку Unicode. Тем не менее, как будет объясняться позже, задаваемый в вызове open режим кодирования приводит к автоматическому выполнению соответствующего декодирования при вводе (и позволяет избежать проблем, которые могут возникнуть из-за чтения неполных последовательностей символов, когда производится чтение блоками байтов):
>>> В = Ь'\хс4\хе8' # Текст, закодированный согласно Latin-1 »> В
Ъ1\xc4\xe8'
>>> len(В) # 2 низкоуровневых байта, 2 закодированных символа
2
»> В.decode(1latin-11) # Декодирование в текст согласно Latin-1 'Аё'
»> В = Ъ'\xc3\x84\xc3\xa8' # Текст, закодированный согласно UTF-8
>>> len (В) # 4 низкоуровневых байта, 2 закодированных символа
4
>>> В.decode('utf-8 ’) # Декодирование в текст согласно UTF-8 'Аё'
>» len (В. decode (’utf-8')) # Два символа Unicode в памяти
2
Другие схемы кодирования
Некоторые кодировки используют для представления символов даже более длинные последовательности байтов. При необходимости вы можете указывать для символов в своих строках 16- и 32-битные значения кодовых точек Unicode. Как было показано ранее, в первом случае мы можем применять "\и. . . " с четырьмя шестнадцатеричными цифрами, а во втором -"\U..."c восемью шестнадцатеричными цифрами, и свободно смешивать их в литералах с более простыми символами ASCII:
»> S = ’A\u00c4B\U000000e8C'
»> s # А, В, С и 2 символа не ASCII
'ААВёС1
>>> len(S) # Длина в пять символов
5
>>> S.encode('latin-1')
b'А\хс4В\хе8С'
>>> len(S.encode('latin-1’)) # 5 байтов при кодировании согласно latin-1
5
>>> S.encode('utf-8')
b'A\xc3\x84B\xc3\xa8C'
>>> len(S.encode('utf-8')) # 7 байтов при кодировании согласно utf-8
1
Формально говоря, вы также можете строить строки Unicode по частям, используя вместо управляющих последовательностей Unicode или шестнадцатеричных управляющих последовательностей встроенную функцию chr, но для крупных строк это может стать утомительным:
»> S = 'А' + chr (0хС4) + 'В' + chr (0хЕ8) + 'С'
>» S 'ААВёС'
Однако другие кодировки могут применять совершенно другие байтовые форматы. Например, кодировка EBCDIC (ср500) даже ASCII не кодирует так, как кодировки, используемые нами до сих пор; поскольку кодирование и декодирование выполняется интерпретатором Python, этот момент нас будет интересовать обычно лишь при предоставлении имен кодировок для источников данных:
»> s
'ААВёС1
»> S.encode('ср500') # Другие две западноевропейские кодировки Ъ'\xclc\xc2T\xc3'
>>> S.encode(1ср850') # 5 байтов каждая, разные закодированные значения b'А\х8еВ\х8аС'
>>> s = 'spam' # В большинстве кодировок текст ASCII остается тем же самым »> S.encode(1 latin-1')
b1 spam'
>>> S.encode('utf-8')
b'spam'
>>> S.encode(1 cp500 1) # Но не в cp500: IBM EBCDIC! b'\xa2\x97\x81\x94'
»> S.encode('cp850') b'spam'
To же самое остается справедливым для кодировок UTF-16 и UTF-32, которые применяют схемы с фиксированными 2 и 4 байтами на символ и заголовками одинаковых размеров — отличающиеся от ASCII символы кодируются по-другому, а символы ASCII занимают не один байт:
>» S = ' A\u00c4B\U000000e8C'
»> S.encode ('utf-16 ')
b'\xff\xfeA\x00\xc4\x00B\x00\xe8\x00C\x00'
>>> S = 'spam'
>>> S.encode('utf-16')
b'\xff\xfes\x00p\x00a\x00m\x00'
>>> S.encode('utf-32')
b'\xff\xfe\xOO\xOOs\xOO\xOO\xOOp\xOO\xOO\xOOa\xOO\xOO\xOOm\xOO\xOO\xOO'
Байтовые строковые литералы: закодированный текст
Здесь также уместно сделать два предостережения. Во-первых, Python З.Х позволяет записывать специальные символы в строках str с помощью шестнадцатеричных управляющих последовательностей и управляющих последовательностей Unicode — в литералах bytes последние принимаются в буквальной форме, не как управляющие последовательности. На самом деле для надлежащего вывода символов не ASCII строки bytes должны быть декодированы в строки str:
»> S = 'А\хС4В\хЕ8С' # Python З.Х: str распознает шестнадцатеричные управляющие »> S # последовательности и управляющие последовательности Unicode
'ААВёС'
»> S = 'A\u00C4B\U000000E8C'
»> S 'ААВёС'
>>> В = b'А\хС4В\хЕ8С' # bytes распознает шестнадцатеричные управляющие
# последовательности, но не управляющие последовательности Unicode b'А\хс4В\хе8С'
>>> В = b'A\u00C4B\U000000E8C' # Управляющие последовательности
# принимаются в буквальной форме!
»> В
b'A\\u00C4B\\U000000E8C'
>>> В = Ь'А\хС4В\хЕ8С' # Использование шестнадцатеричных управляющих
# последовательностей для bytes
>» В # Отличающиеся от ASCII символы выводятся
# как шестнадцатеричные цифры
b'А\хс4В\хе8С'
>» print (В)
b'А\хс4В\хе8С'
>>> В.decode(1latin-1') # Декодирование согласно latin-1 для
# интерпретации как текста
'ААВёС'
Во-вторых, литералы bytes требуют либо символов ASCII, либо управляющих последовательностей, если значения превышают 127. С другой стороны, строки str допускают литералы, содержащие любой символ из исходного набора символов, которым, как обсуждалось ранее, по умолчанию будет UTF-8 в Python З.Х (и ASCII в Python
2.Х), если только в исходном файле не указано объявление кодировки:
>>> S = 'ААВеС' # Символы из UTF-8, если отсутствует
# объявление кодировки
»> S 'ААВёС'
»> В = Ь1 ААВёС'
SyntaxError: bytes can only contain ASCII literal characters.
Синтаксическая ошибка: строка bytes может содержать только литеральные символы ASCII.
»> В = b' А\хС4В\хЕ8С' # Символы должны быть ASCII или управляющими
# последовательностями
»> В
b'А\хс4В\хе8С'
>>> В.decode('latin-1')
'ААВёС'
>>> S.encode() # По умолчанию исходный код кодируется согласно UTF-8
b'А\хсЗ\х84В\хсЗ\ха8С' # Используется стандартная кодировка системы,
# если она не передана явно
»> S.encode ('utf-8')
b'А\хсЗ\х84В\хсЗ\ха8С'
>>> В.decode() # Низкоуровневые байты не соответствуют utf-8
UnicodeDecodeError: 'utf8' codec can't decode bytes in position 1-2: ... Ошибка декодирования Unicode: кодек utf8 не может декодировать байты в позициях 1-2: ...
Оба ограничения обретут смысл, когда вы вспомните, что байтовые строки хранят данные, основанные на байтах, не декодированные порядковые значения кодовых точек Unicode. Наряду с тем, что они могут содержать закодированную форму текста, декодированные значения кодовых точек не вполне применимы к байтовым строкам, если только символы предварительно не закодированы.
Преобразования между кодировками
До сих пор мы кодировали и декодировали строки для инспектирования их структуры. Строку можно также преобразовывать в кодировку, отличающуюся от первоначальной, но потребуется предоставить явное имя кодировки для кодирования и декодирования. Так поступать необходимо вне зависимости от того, откуда поступает исходная текстовая строка — из файла либо из литерала.
Термин преобразование может здесь использоваться не совсем правильно — в действительности он означает всего лишь кодирование текстовой строки в низкоуровневые байты по другой схеме, а не той, из которой производилось декодирование. Как подчеркивалось ранее, декодированный текст в памяти не имеет типа кодировки и является просто строкой из кодовых точек Unicode (они же символы); концепция изменения его кодировки в такой форме отсутствует. Тем не менее, эта схема позволяет сценариям читать данные в одной кодировке и сохранять их в другой, чтобы поддерживать множество клиентов для тех же самых данных:
>>> В = b'А\хсЗ\х84В\хсЗ\ха8С # Текст, первоначально закодированный в
# формате UTF-8
»> S = В.decode('utf-8') # Декодирование в текст Unicode согласно UTF-8
»> S
'ААВёС'
>>> Т = S. encode ('ср500 ') # Преобразование в закодированную строку bytes
# согласно EBCDIC
»> Т
b'\xclc\xc2T\xc3'
>>> U = Т. decode (' ср500 ') # Преобразование обратно в Unicode согласно EBCDIC
»> и
'ААВёС'
»> U.encode() # Снова согласно стандартной кодировке utf-8
Ъ'А\хсЗ\х84В\хсЗ\ха8С'
Имейте в виду, что специальные управляющие последовательности Unicode и шестнадцатеричные управляющие последовательности необходимы только при ручном написании строк Unicode, отличающихся от ASCII. На практике вы будете часто загружать такой текст из файлов. Как будет показано далее в главе, файловый объект Python З.Х (создаваемый посредством встроенной функции open) автоматически декодирует текстовые строки при чтении и кодирует их при записи; по этой причине ваш сценарий нередко может иметь дело со строками обобщенным образом, не представляя специальные символы напрямую.
Позже в главе вы также увидите, что преобразования между кодировками можно осуществлять во время передачи строк в файлы и из файлов, применяя методику, которая очень похожа на используемую в последнем примере. Хотя при открытии файла вам все равно придется указывать явные имена кодировок, файловый интерфейс выполняет большую часть работы по преобразованию автоматически.
Кодирование строк Unicode в Python 2.Х
В настоящей главе я делаю акцент на поддержке Unicode в Python З.Х, поскольку она относительно нова. Но теперь, когда были изложены основы строк Unicode в Python
З.Х, я должен более полно объяснить, как добиться многого того же в Python 2.Х, хотя инструменты и отличаются. Тип Unicode доступен в Python 2.Х, но является отдельным от str, поддерживает большинство тех же самых операций и разрешает смешивание нормальных строк и строк Unicode, когда объект str содержит все символы ASCII.
Фактически вы можете считать str из Python 2.Х типом bytes из Python З.Х, когда дело доходит до декодирования низкоуровневых байтов в строку Unicode, при условии, что они имеют надлежащую форму Ниже приведен интерактивный сеанс Python 2.Х; в Python 2.Х символы Unicode отображаются в шестнадцатеричном виде, если только не выводятся явно, а отображение символов не ASCII может варьироваться в зависимости от оболочки (большинство кода в текущем разделе запускалось вне IDE-среды IDLE, которая временами обнаруживает и выводит символы Latin-1 в закодированных байтовых строках — позже будет описана переменная среды PYTHONIOENCODING и проблемы отображения в окне командной строки Windows):
C:\code> C:\python27\python
»> S = 1 А\хС4В\хЕ8С' # Строка 8-битных байтов
»> S # Текст, закодированный согласно Latin-1,
# некоторые символы не ASCII
'А\хс4В\хе8С'
>>> print S # Непечатаемые символы (в IDLE может быть по-друтому)
АиВиС
»> U = S.decode ('latinl') # Декодирование bytes в текст Unicode согласно latin-1
»> и
и'А\хс4В\хе8С’
>>> print U
ААВёС
>>> S.decode ('utf-8 ') # Закодированная форма не совместима с utf-8 UnicodeDecodeError: 'utf8' codec can't decode byte 0xc4 in position 1: invalid continuation byte
Ошибка декодирования Unicode: кодек utf8 не может декодировать байт 0хс4 в позиции 1: недопустимый байт продолжения
»> S.decode('ascii') # Закодированные байты также выходят за пределы
# диапазона ASCII UnicodeDecodeError: 'ascii' codec can't decode byte 0xc4 in position 1: ordinal not in range(128)
Ошибка декодирования Unicode: кодек ascii не может декодировать байт 0хс4 в позиции 1: порядковое значение не в диапазоне range(128)
Для написания текста Unicode создайте объект Unicode с помощью литеральной формы и' ххх ’ (как уже упоминалось, начиная с версии Python 3.3, он снова стал доступным, но в целом избыточен в Python З.Х, т.к. нормальные строки поддерживают Unicode):
>>> U = и'А\хС4В\хЕ8С' # Создать строку Unicode, шестнадцатеричные
# управляющие последовательности
»> и
и'А\хс4В\хе8С'
»> print U
'ААВёС'
После создания объекта Unicode вы можете преобразовывать текст Unicode в другие кодировки с низкоуровневыми байтами подобно кодированию объектов str в объекты bytes в Python З.Х:
»> U.encode('latin-1') # Кодирование согласно latin-1: 8-битные байты 'А\хс4В\хе8С'
»> U.encode('utf-8') # Кодирование согласно utf-8: многобайтовая строка 'А\хсЗ\х84В\хсЗ\ха8С'
В точности как в Python З.Х, в Python 2.Х символы не ASCII могут записываться посредством шестнадцатеричных управляющих последовательностей или управляющих последовательностей Unicode. Однако, как и в случае типа bytes из Python З.Х, управляющие последовательности и\и. . ." и "\U. . . " в Python 2.Х распознаются только для строк Unicode, но не 8-битных строк str — к тому же они используются для предоставления значений декодированных порядковых целых чисел Unicode, которые не имеют смысла в строке с низкоуровневыми байтами:
С:\code> С:\python27\python
>>> U = и'А\хС4В\хЕ8С' # Шестнадцатеричные управляющие
# последовательности для символов не ASCII
»> и
и'А\хс4В\хе8С1 »> print U
ААВёС
»> U = и1 A\u00C4B\U000000E8C'
# Управляющие последовательности Unicode
# для символов не ASCII
# и ' ' = 1 6 бит, U' ' = 32 бита
»> и
и'А\хс4В\хе8С'
>» print U
ААВёС
>» S = 1 А\хС4В\хЕ8С *
# Шестнадцатеричные управляющие последовательности работают »> S
'А\хс4В\хе8С'
>>> print S # Но некоторые могут выводится странно,
# не будучи декодированными
А®ВвС
>>> print S.decode(’latin-1')
ААВёС
»> S = IA\u00C4B\U000000E8Cl # He управляющие последовательности Unicode:
# берутся буквально!
>» S
'А\\и00С4В\\и000000Е8С'
>» print S A\u00C4B\U000000E8C >>> len(S)
19
Смешивание строковых типов в Python 2.Х
Подобно типам str и bytes из Python З.Х типы Unicode и str из Python 2.Х разделяют почти идентичные наборы операций, так что при отсутствии необходимости преобразования в другие кодировки вы часто можете трактовать тип Unicode так, как если бы он был str. Тем не менее, одно из основных отличий между линейками Python 2.Х и Python З.Х заключается в том, что объекты Unicode и отличающиеся от Unicode объекты str можно свободно смешивать в выражениях Python 2.Х — до тех пор, пока объект str совместим с объектом Unicode, интерпретатор Python будет автоматически преобразовывать его в Unicode:
>>> u’ab’ + ’ cd’ # В случае совместимости в Python 2.Х можно смешивать
u'abed' # Но 'ab' + b'cd' в Python З.Х не разрешено
Однако такой либеральный подход к смешиванию строковых типов в Python 2.Х работает, только если 8-битные строки содержат исключительно 7-битные (ASCII) байты:
>>> S = 'А\хС4В\хЕ8Сf # Нельзя смешивать в Python 2.Х,
# если str содержит символы не ASCII!
»> U = и' А\хС4В\хЕ8С '
»> S + и
UnicodeDecodeError: 'ascii' codec can't decode byte 0xc4 in position 1: ordinal not in range (128)
Ошибка декодирования Unicode: кодек ascii не может декодировать байт 0хс4 в позиции 1: порядковое значение не в диапазоне range(128)
»> 'abc' + U # Можно смешивать, только если str содержит
# все 7-битные символы ASCII
u'abcA\xc4B\xe8C'
»> print 'abc' + U # Использовать print для отображения символов
аЬсААВёС
»> S.decode ('latin-1') + U # В Python 2.Х также может потребоваться
# ручное преобразование
и'А\хс4В\хе8СА\хс4В\хе8С'
>>> print S.decode(1latin-1') + U
ААВёСААВёС
>>> print u'\xA3' + '999.99' # См. также пример с денежными значениями
# из главы 25 первого тома
£ 999.99
Напротив, в Python З.Х типы str и bytes никогда автоматически не смешиваются и требуют ручных преобразований — фактически предыдущий код выполняется в Python 3.3 и последующих версиях, но лишь потому, что интерпретатор Python З.Х считает литерал Unicode из Python 2.Х таким же, как нормальная строка (и игнорируется); эквивалентом Python З.Х могло бы быть сложение объектов str и bytes (т.е. ' ab' +b' cd1), что потерпит неудачу в Python З.Х, если не преобразовать объекты в общий тип.
Тем не менее, в Python 2.Х несходство типов часто несущественно для кода. Подобно нормальным строкам строки Unicode можно сцеплять, индексировать, нарезать, сопоставлять с образцом посредством модуля ге и т.д., к тому же их нельзя изменять на месте. Если вам когда-либо понадобится явно выполнять преобразования между двумя типами, тогда можете применять встроенные функции str и Unicode:
»> str(u'spam') # Unicode в нормальную
'spam'
>>> Unicode (' spam') # Нормальная в Unicode
u' spam'
Если вы используете Python 2.Х, тогда просмотрите пример другого файлового интерфейса, приводимый позже в главе. Вызов open поддерживает только файлы 8-битных байтов, возвращая их содержимое в виде строк str, которое вы можете по своему усмотрению интерпретировать как текстовые или двоичные данные и при необходимости декодировать его. Для чтения и записи файлов Unicode и автоматического кодирования либо декодирования их содержимого применяйте в Python 2.Х вызов codecs. open, который вы увидите в действии далее в главе. Упомянутый вызов обеспечивает почти такую же функциональность, как open в Python З.Х, и для представления содержимого файла использует объекты Unicode из Python 2.Х. При чтении файла закодированные байты транслируются в декодированные символы Unicode, а при записи строки переводятся в желаемую кодировку, которая была указана во время открытия файла.
Объявления кодировок в файлах исходного кода
Наконец, управляющие последовательности Unicode хорошо подходят для редких символов Unicode в строковых литералах, но могут стать утомительными, когда внедрять в строки отличающийся от ASCII текст приходится часто. Чтобы интерпретировать содержимое строк, которые вы записываете и, следовательно, внедряете внутрь текста своих файлов сценариев, в качестве стандартной кодировки Python применяет UTF-8 в линейке Python З.Х (и ASCII в линейке Python 2.Х). Однако Python Python позволяет использовать произвольные кодировки и поддерживаемые ими наборы символов за счет включения комментария, в котором указывается желаемая кодировка. Комментарий обычно имеет показанную ниже форму и должен находиться в первой или во второй строке сценария в Python 2.Х или З.Х:
# coding: latin-1
При наличии комментария такого вида интерпретатор Python будет распознавать строки, изначально представленные в заданной кодировке. Это означает, что вы можете редактировать свой файл сценария в текстовом редакторе, который корректно воспринимает и отображает диакритические и другие символы не ASCII, а интерпретатор Python будет правильно их декодировать в строковых литералах. Например, взгляните, как комментарий в начале файла text.py с представленным далее содержимым позволяет встраивать символы Latin-1 в строки, которые сами встраиваются в текст файла сценария:
# coding: latin-1
# Любая из следующих форм литеральных строк работает в latin-1.
# Изменение кодировки выше на либо ascii, либо utf-8 приведет к неудаче,
# потому что тогда Охе4 и Охе8 в myStrl не будут допустимыми.
myStrl = 'ААВёС'
myStr2 = ' A\uOOc4B\UOOOOOOe8C'
myStr3 = 'A' -i- chr(0xC4) + 'В' + chr(0xE8) + 'С'
import sys
print('Default encoding:', sys.getdefaultencoding()) # Стандартная кодировка
for aStr in myStrl, myStr2, myStr3:
print('{0}, strlen={l}, '. format(aStr, len(aStr)), end='')
bytesl = aStr.encode() # Согласно стандартной кодировке utf-8:
# 2 байта для не ASCII bytes2 = aStr.encode(’latin-1') # Один байт на символ
# bytes3 = aStr.encode(’ascii') # ASCII потерпит неудачу:
# за пределами диапазона 0..127
print('byteslenl={0}, bytes1еп2={1}'.format(len(bytesl), len(bytes2)))
Запуск сценария приводит к выдаче следующего вывода, где для каждой из трех методик кодирования отображается строка, ее длина и длины их форм байтовых строк, закодированных согласно UTF-8 и Latin-1:
C:\code> C:\python37\python text.py
Default encoding: utf-8
аАВёС, strlen=5, byteslenl~7, byteslen2~5 ААВёС, strlen=5, byteslenl=7, byteslen2=5 ААВёС, strlen=5, byteslenl=7, byteslen2=5
Поскольку многие программисты, вероятно, будут придерживаться стандартных кодировок исходного кода, я предлагаю поискать дополнительные сведения об этом варианте и о других расширенных темах поддержки Unicode в наборе руководств по Python. Здесь же мы кратко рассмотрим новые типы объектов байтовых строк в Python З.Х и затем перейдем к исследованию изменений файлов и инструментов.
Добавочные примеры написания символов не ASCII и объявлений в файлах исходного кода можно найти в сценарии форматировании денежных значений из главы 25 первого тома и в файле formats_currency2 .ру, входящем в состав загружаемого кода для настоящей книги. Последний требует объявления в файле исходного кода, чтобы с ним мог работать интерпретатор Python, т.к. он содержит символы валют, отличающиеся от ASCII. Данный пример также иллюстрирует улучшение переносимости, возможное в случае применения литерала Unicode из Python 2.Х в коде Python З.Х, начиная с версии Python 3.3.
ж
Использование объектов bytes в Python З.Х
В главе 7 первого тома мы исследовали широкий выбор операций, доступных для универсального строкового типа str в Python З.Х; базовый строковый тип в линейках Python 2.Х и Python З.Х работает идентично, поэтому мы не будем возвращаться к данной теме. Взамен давайте чуть глубже проанализируем набор операций, предлагаемый новым типом bytes в Python З.Х.
Как упоминалось ранее, объект bytes в Python З.Х представляет собой последовательность коротких целых чисел, каждое из которых находится в диапазоне 0-255 и потому отображается как символ ASCII. Он поддерживает операции над последовательностями и большинство методов, доступных в объектах str (и присутствующих в типе str из Python 2.Х). Тем не менее, bytes не поддерживает метод format или выражение форматирования %, к тому же смешивать объекты типов bytes и str не допускается без явных преобразований. Обычно вы будете использовать все объекты типа str и текстовые файлы для текстовых данных, а все объекты типа bytes и двоичные файлы для двоичных данных.
Вызовы методов
Если вы действительно хотите увидеть, какие атрибуты имеет тип str, которые отсутствуют в bytes, то всегда можете проверить результаты вызова встроенной функции dir. Вывод также может сообщить кое что о поддерживаемых ими операциях выражений (например,_mod_и_rmod_реализуют операцию %):
С:\code> С:\python37\python
# Атрибуты, имеющиеся в str, но не в bytes
»> set (dir (1 abc')) - set (dir (b'abc'))
{'isprintable', 'format', 'isdecimal', ’encode1, 'format_map',
'casefold', 'isidentifier', 'isnumeric'}
# Атрибуты, имеющиеся в bytes, но не в str
»> set (dir (b ’ abc')) - set (dir (' abc1))
{'fromhex', 'decode', 'hex'}
Как видите, функциональность типов str и bytes почти идентична. Их уникальные атрибуты, как правило, являются методами, которые применимы к одному типу, но неприменимы к другому; скажем, decode транслирует низкоуровневый объект bytes в его представление str, a encode переводит строку в ее низкоуровневое представление bytes. Большинство методов одинаковы, хотя методы bytes требуют аргументов bytes (опять-таки строковые типы Python З.Х не смешиваются). Также вспомните, что объекты bytes неизменяемы как и объекты str в Python 2.Х и З.Х (для краткости сообщения об ошибках далее показаны не полностью):
>>> В = b'spam1 # Литерал Ь'. . . ' типа bytes
»> B.fincMb’pa')
1
>» В.replace (b’pa’ , b'XY') # Методы bytes ожидают аргументов bytes b’sXYm'
>>> В.split (b'pa1) # Методы bytes возвращают результаты bytes
[bfs', b'm']
>» В b'spam'
»> B[0] = 'x'
TypeError: 'bytes' object does not support item assignment
Ошибка типа: объект bytes не поддерживает присваивание в отношении элементов
Одно заметное отличие заключается в том, что строковое форматирование в Python З.Х работает только с объектами str, но не с объектами bytes (выражения и методы строкового форматирования обсуждались в главе 7 первого тома):
»> '%s' % 99
' 99'
»> b' %s' % 99
TypeError: unsupported operand type(s) for %: 'bytes’ and 'int'
Ошибка типа: неподдерживаемые типы операндов для %: bytes и int
>» ’{0} ’ .format (99)
’ 99'
>>> b1{0}’.format(99)
AttributeError: ’bytes' object has no attribute 'format'
Ошибка атрибута: объект bytes не имеет атрибута format
Операции над последовательностями
Помимо вызовов методов все обычные универсальные операции над последовательностями, которые вы знаете (и возможно предпочитаете) по строкам и спискам Python 2.Х, работают ожидаемым образом для типов str и bytes в Python З.Х; к ним относятся индексирование, нарезание, конкатенация и т.д. В показанном ниже взаимодействии обратите внимание, что индексирование объекта bytes возвращает целое число, дающее двоичное значение байта; на самом деле bytes представляет собой последовательность 8-битных целых чисел, которая при отображении целиком для удобства выводится как строка символов ASCII, когда это возможно. Чтобы проверить значение заданного байта, используйте встроенную функцию chr для его преобразования в соответствующий символ:
>>> В = b'spam' # Последовательность коротких целых чисел
»> В # Выводится как символы ASCII (и/или шестнадцатеричные
# управляющие последовательности)
b1 spam'
>>> В[0] # Индексирование выдает целое число
115
»> В[-1]
# Отображает символ для целого числа
# Отображает целые значения всех байтов
>» chr (В [0])
1 s'
»> list (В)
[115, 112, 97, 109]
»> В [1: ] , В [: -1]
(b'pam', Ь'spa')
>>> len(В)
4
>» В + b'lmn'
Ъ'spamlmn'
>>> В * 4
b'spamspamspamspam'
Другие способы создания объектов bytes
До сих пор мы создавали объекты bytes главным образом с помощью синтаксиса литералов b' . . . '. Мы также можем создавать их вызовом конструктора bytes с объектом str и именем кодировки, вызовом конструктора bytes с итерируемым объектом целых чисел, представляющих значения байтов, или кодированием объекта str согласно стандартной (или переданной) кодировки. Как мы видели, операция кодирования берет текстовый объект str и возвращает низкоуровневые значения закодированных байтов строки в соответствии с указанной кодировкой; и наоборот, операция декодирования берет низкоуровневую последовательность bytes и транслирует ее в представление текстовой строки str — последовательность символов Unicode. Обе операции создают новые строковые объекты:
>>> В = b’abc* # Литерал
>» в
b'abc'
»> В = bytes ('abc' , 'ascii') # Конструктор с именем кодировки
»> в Ъ'abc'
>» ord( 'а')
97
>» В = bytes ([97, 98, 99]) # Итерируемый объект с целыми числами »> В b'abc'
>>> В = 'spam'.encode() # str.encodef) (илиЬуЬеэО)
>» В b1 spam'
>>>
# bytes .decode () (или str())
»> S = В.decode()
>» S 'spam'
С функциональной точки зрения последние две операции из показанных выше в действительности являются инструментами для преобразований между str и bytes, о которых шла речь ранее и которые более подробно рассматриваются в следующем разделе.
Смешивание строковых типов
В вызов replace из раздела “Вызовы методов” ранее в главе мы должны были передать два объекта bytes — типы str в нем не работают. Несмотря на то что в Python
2.Х автоматически выполняются преобразования str в и из Unicode, когда они возможны (т.е. когда str содержит 7-битный текст ASCII), Python З.Х в ряде контекстов требует определенных строковых типов и ожидает ручных преобразований, если они необходимы:
# Вызовам методов и функций должны передаваться ожидаемые типы »> В = b'spam'
>>> В.replace ('ра1 , 'XY')
TypeError: expected an object with the buffer interface Ошибка типа: ожидался объект с интерфейсом буфера
»> В. replace (Ь1 pa' , b'XY')
b'sXYm'
>>> В = В' spam1
>>> В. replace (bytes ('pa') , bytes ('xy'))
TypeError: string argument without an encoding Ошибка типа: строковый аргумент без кодировки
»> В. replace (bytes (' pa' , ’ascii1), bytes ('xy1 , 'utf-8'))
b'sxym'
# В выражениях со смешанными типами Python З.Х должны выполняться
# ручные преобразования
>» b'ab' + 'cd'
TypeError: can't concat bytes to str
Ошибка типа: не удалось выполнить конкатенацию bytes и str
»> b'ab' .decode() + 'cd' # bytes в str
1 abed'
»> b'ab' + ' cd'.encode () # str в bytes
b'abed'
>» b'ab' + bytes ('cd' , 'ascii') # str в bytes b'abed'
Хотя вы можете самостоятельно создавать объекты bytes для представления упакованных двоичных данных, они также могут порождаться автоматически при чтении файлов, открытых в двоичном режиме, как будет показано далее в главе. Но сначала давайте займемся родственным bytes типом, допускающим изменения на месте.
Назад: Проектирование с использованием исключений
Дальше: Управляемые атрибуты