Катастрофа Unicode в Python3 / Хабр

От переводчика: Armin Ronacher довольно известный разработчик в Python-сообществе(Flask,jinja2,werkzeug).
Он довольно давно начал своеобразный крестовый поход против Python3, но обвинить его в истерике и ретроградстве не так-то просто: его возражения продиктованы серьезным опытом разработки, он довольно подробно аргументирует свою точку зрения. Немного о терминологии:
coercion я перевел как принудительное преобразование кодировок, а byte string как байтовые строки, так как термин «сырые» строки(raw string) все же означает несколько иное.
«Историческое» примечание: в 2012 г. Армин предложил PEP 414, который содержал ряд мер по устранению проблем с Unicode, PEP подтвердили довольно быстро, однако воз и ныне там, так как нижеприведенный текст написан 5 января 2014 года

Все труднее становиться вести обоснованную дискуссию о различиях между Python 2 и 3, так как один язык уже мертв,
а второй активно развивается. Когда кто-либо начинает обсуждение поддержки Unicode в двух ветках Python — это весьма сложная тема. Вместо рассмотрения поддержки Unicode в двух версиях языка, я рассмотрю базовую модель обработки текста и байтовых строк.

В данном посте я покажу на примере решений разработчиков языка и стандартной библиотеки,
что Python 2 лучше подходит для работы с текстом и байтовыми строками.

С тех пор как мне пришлось сопровождать большое количество кода, который напрямую работал с преобразованием между байтовыми строками и Unicode, ухудшения, произошедшие в Python3, вызвали у меня много печали. Особенно меня раздражают материалы основной команды разработчиков python, которые призывают меня верить, что python 3 лучше 2.7.

Модель представления текста

Главное различие между Python 2 и Python 3 —базовые типы, существующие для работы со строками и байтовыми строками. В Python 3 мы имеем один строковый тип: str, который хранит данные в Unicode, и два байтовых типа: bytes и bytearray.

С другой стороны, в Python 2 у нас есть два строковых типа: str, который достаточен для любых целей и задач, ограниченных строками в кодировке ASCII + некоторыми неопределенными данными, превышающими интервал в 7 бит. Вместе с типом str у Python2 есть тип данныхunicode, эквивалентный типу данных str Python 3. Для работы с байтами в Python 2 есть один тип:bytearray, взятый из Python 3. Присмотревшись к ситуации, вы можете заметить, что из Python 3 кое-что удалили: поддержку строковых данных не в юникоде.Компенсацией жертвоприношения стал хешируемый байтовый тип данных(bytes). Тип данных bytarray изменяемый, а поэтому он не может быть хеширован. Я очень редко, использую бинарные данные как ключи словаря, а потому возможность или невозможность хеширования бинарных данных не кажется мне очень серьезной. Особенно в Python 2, так как байты могут быть без каких-либо проблем помещены в переменную типа str.

Потерянный тип

Из Python 3 исключают поддержку байтовых строк, которые в ветке 2.x были типом str. На бумаге в этом решении нет ничего плохого. С академической точки зрения строки, всегда представленные в юникоде, это прекрасно. И это действительно так, если целый мир — это ваш интерпретатор. К сожалению, в реальном мире, все происходит по-другому: вы вынуждены регулярно работать с разными кодировками, в этом случае подход Python 3 к работе со строками трещит по швам.

Буду честен перед вами: то как Python 2 обрабатывает Unicode провоцирует ошибки, и я полностью одобряю улучшения обработки Unicode. Моя позиция в том, что, то как это делается в Python 3, является шагом назад и порождает еще больше ошибок, а потому я абсолютно ненавижу работать с Python 3.

Ошибки при работе с Unicode

Прежде чем я погружусь в детали, мы должны понять разницу поддержки Unicode в Python 2 и 3,
а так же то, почему разработчики приняли решение поменять механизм поддержки Unicode.

Изначально Python 2 как и многие иные языки до него создавался без поддержки обработки сток разных кодировок.
Строка и есть строка, она содержит байты. Это требовало от разработчиков корректно работать с различными
кодировками вручную. Это было вполне приемлемо для многих ситуаций. Многие годы веб-фреймворк Django
не работал с Unicode, а использовал исключительно байтовые строки.

Тем временем Python 2 годами улучшал внутреннюю поддержку Unicode. Улучшение поддержки Unicode
позволяло использовать его для единообразного представления данных в различных кодировках.

Подход к обработке строк, использующих определенную кодировку, в Python 2 довольно прост:
вы берете строку (байтовую), которую вы могли получить откуда угодно, а затем преобразуете
ее из той кодировки, которая характерна для источника строки(метаданные, заголовки, иные)
в строку Unicode. Став Unicode строкой, она поддерживает все те же операции
что и байтовая, но теперь она может хранить больший диапазон символов.
Когда вам необходимо передать строку на обработку куда-либо еще, то вы снова
преобразуете ее в ту кодировку, которая используется принимающей стороной,
и перед нами вновь байтовая строка

Какие же особенности связаны с таким подходом? Для того, чтобы это работало на уровне ядра языка,
Python 2 должен предоставлять способ перехода из мира без Unicode в прекрасный мир с Unicode.
Это возможно благодаря принудительному преобразованию байтовых и небайтовых строк. Когда это происходит
и как этот механизм работает?

Основной момент заключается в том, что когда байтовая строка участвует в одной операции с Unicode строкой,
то байтовая строка преобразуется в Unicode строку при помощи неявного процесса декодирования строки, который использует кодировку «по умолчанию». Данной кодировкой по умолчанию считается ASCII. Python предоставлял возможность менять кодировку по умолчанию, используя один модуль, но теперь из модуля site.py удалили функции для изменения кодировки по умолчанию, она устанавливается в ASCII. Если запустить интерпретатор с флагом -s, то функция sys.setdefaultencoding будет вам доступна и вы сможете поэкспериментировать, чтобы выяснить что произойдет, если вы выставите кодировкой по умолчанию UTF-8. В некоторых ситуациях при работе с кодировкой по умолчанию могут возникнуть проблемы:

1. неявное задание и преобразование кодировки при конкатенации:

Здесь левая строка преобразуется, используя кодировку «по умолчанию», в Unicode строку. Если строка содержит не ASCII символы, то при нормальной ситуации выполнения программы преобразование останавливается с выбросом исключения UnicodeDecodeError, так как кодировка по умолчанию — ASCII

2. Неявное задание и преобразование кодировки при сравнении строк

Это звучит опаснее чем есть на самом деле. Левая часть преобразуется в Unicode, а затем происходит сравнение. В случае, если левая сторона не может быть преобразована, интерпретатор выдает предупреждение, а строки считаются неравными(возвращается False в качестве результата сравнения). Это вполне здравое поведение, если даже при первом знакомстве с ним так не кажется.

3. Явное задание и преобразование кодировки, как часть механизма с использованием кодеков.

Это одна из наиболее зловещих вещей и наиболее распостраненный источник всех неудач и недопониманий Unicode в Python 2. Для предоления проблем в этой области в Python 3 предприняли безумный шаг, удалив метод .decode() у Unicode строк и метод .encode() у байтовых строк, это вызвало наибольшее непонимание и досаду у меня. С моей точки зрения это очень глупое решение, но мне много раз говорили что это я ничего не понимаю, возврата назад не будет.

Явное преобразование кодировки при работе с кодеками выглядит так:

Это строка, очевидно, является байтовой строкой. Мы требуем ее преобразовать в UTF-8. Само по себе эnо бессмысленно, так как UTF-8 кодек преобразует строку из Unicode в байтовую строку с кодировкой UTF-8. Как же это работает? UTF-8 кодек видит, что строка не является Unicode строка, а поэтому сначала выполняется принудительное преобразование к Unicode. Пока «foo» только ASCII данные и кодировка по умолчанию ASCII, принудительное преобразование происходит успешно, а уже после этого Unicode строка u«foo» преобразуется в UTF-8.

Механизм кодеков

Теперь вы знаете что Python 2 имеет два подхода к представлению строк: байтами и Unicode. Преобразование между этими представлениями осуществляется при помощи механизма кодеков. Данный механизм не навязывает схему преобразования Unicode->byte или на нее похожую. Кодек может производить преобразование byte->byte или Unicode->Unicode. Фактически система кодеков может реализовывать преобразование между любыми типами Python. Вы можете иметь JSON кодек, который производит преобразование строки в сложный Python объект на ее основе, если сочтете, что такое преобразование вам необходимо.

Такое положение дел может вызвать проблемы с пониманием механизма, начиная с его основ. Примером этого может быть кодек с названием ‘undefined’, который может быть установлен в качестве кодировки по умолчанию. В этом случае любые принудительные преобразования кодировок строк будут отключены:

И как же в Python 3 решают проблему с кодеками? Python 3 удаляет все кодеки, которые не выполняют преобразования вида: Unicode byte, а кроме того уже ненужные сейчас метод байтовых строк .encode() и строковый метод .decode(). Это очень плохое решение, так как было очень
много полезных кодеков. Например очень распространено использовать преобразование с помощью hex кодека в Python 2:

Пока вы можете сказать, что в данном конкретном случае задача может быть решена при помощи модуля подобного binascii, но проблема более глубока, модули с кодеками доступны отдельно. Например библиотеки, реализующие чтение из сокетов, используют кодеки для частичного преобразования данных из потоков данных библиотеки zlib:

В конце концов, проблема была признана и в Python 3.3 восстановили эти кодеки. Однако сейчас мы снова вводим пользователя в неразбериху, так как кодеки до вызова функций не предоставляют метаинформации о тех типа, которые они могут обработать. По этой причине Python теперь может выбрасывать следующие исключения:

(Обратите внимание, что кодек теперь называется zlib_codec вместо zlib, так как Python 3.3 не сохранил старых обозначений для кодеков)

А что произойдет, если мы вернем назад метод .encode() для байтовых строк, например? Это легко проверить даже без хаков интерпретатора Python. Напишем функцию с аналогичным поведением:

Теперь мы можем использовать эту функцию как замену метода .encode() байтовых строк:

Ага! Python 3 уже умеет работать с такой ситуацией. Мы получаем красивое оповещение об ошибке. Я считаю, что даже “Can’t convert ‘bytes’ object to str implicitly” гораздо лучше и понятней чем “’bytes’ object has no attribute ‘encode’”.

Почему бы не вернуть эти методы преобразования кодировки(encode и decode) назад? Я действительно не знаю и не думаю больше об этом. Мне уже многократно объясняли что я ничего не понимаю и я не понимаю новичков, или, то что «текстовая модель» изменилась и мои требования к ней бессмысленны.

Байтовые строки потеряны

Теперь вслед за регрессией системы кодеков изменились и строковые операции: они определены лишь для строк Unicode. На первый взгляд это выглядит вполне разумно, но на самом деле это не так. Раньше интерпретатор имел реализации для операций над байтовыми и Unicode строками. Этот подход был совершенно очевиден дял программистов, если объекту нужно было иметь представление в виде байтовой или Unicode строки, определялось два метода:__str__ and __unicode__. Да, конечно, использовалось принудительное изменение кодировки, которое смущало новичков, но зато у нас был выбор.

Почему это полезно? Потому что, к примеру, если вы работаете с низкоуровневыми протоколами, вам часто необходимо иметь дело с числами в определенном фоормате внутри байтовой строки.

Собственная система контроля версий, используемая разработчиками Python, не работает на Python 3, потому что годами команда разработки Python не хочет вернуть возможность форматирования для байтовых строк.

Все вышеописанное показывает: модель обработки строковых данных Python 3 не работает в реальном мире. К примеру в Python 3 «обновили» некоторые API, сделав их работающими только с Unicode, а потому они полностью непригодны для применения в реальных рабочих ситуациях. К примеру теперь вы не можете больше анализировать байты с помощью стандартной библиотеки, но только URL. Причина этого в неявном предположении, что все URL представлены лишь в Unicode (при таком положении дел вы уже не сможете работать с почтовыми сообщениями в не Unicode кодировке, если не будете полностью игнорировать существование бинарных вложений в письмо).

Раньше такое было довольно легко исправить, но так как ныне байтовые строки потеряны для разработчиков, библиотека обработки URL имеет ныне две реализации. Одна для Unicode, а вторая для байтовых объектов. Две реализации для одной и той же функции ведут к тому что результат обработки данных может быть очень разным:

Выглядит достаточно похоже? Вовсе нет, потому что в результате мы имеем совершенно разные типы данных у результата операции.
Один из них это кортеж строк, второй больше похож на массив целых чисел. Я уже писал об этом ранее и подобное состояние вызывает у меня страдания. Теперь написание кода на Python доставляет мне серьезный дискомфорт или становится крайне неэффективным, так как вам теперь приходится пройти через большое количество преобразований кодировок данных. Из-за этого становится очень сложно писать код, реализующий все необходимые функции. Идея что все Unicode очень хороша в теории, но полностью неприменима на практике.

Python 3 пронизан кучей костылей для обработки ситуаций, где обрабатывать Unicode невозможно, а у таких как я, которые много работают с такими ситуациями, все это вызывает жуткое раздражение.

Наши костыли не работают

Поддержка Unicode в ветке 2.х неидеальна и далека от идеала. Это отсутсвующие API, проблемы, приходящие с разных сторон, но мы как программисты делали все это рабочим. Многие методы, которыми мы это делали ранее больше невозможно применить в Python 3, а некоторые API будут изменены, чтобы хорошо работать с Python 3.

Мой любимый пример это обработка файловых потоков, которые могли быть как байтовыми, так и текстовыми, но не было надежного метода определить какой перед нами тип потока. Трюк, который я помог популяризировать это чтение нулевого количества байт из потока для определения его типа. Теперь этот трюк не работает. К примеру передача объекта запроса библиотеки urllib функции Flask, которая обрабатывает JSON, не работает в Python 3, но работает в Python 2:

В ходе обработки выбрашенного исключения выбрасывается еще одно:

И что же?

Кроме тех проблем, что я описал выше у Python 3 с поддержкой Unicode есть и куча других проблем. Я начал отписываться от твиттеров разработчиков Python потому что мне надоело читать какой Python 3 замечательный, так как это противоречит моему опыту. Да, в Python 3 много плюшек, но то как поступили с обработкой байтовых строк и Unicode к ним не относится.

(Хуже всего то, что многие действительно крутые возможности Python 3 обычно столь же хорошо работают и в Python 2. Например yield from, nonlocal, поддержка SNI SSL и т.д. )

В свете того, что только 3% разработчиков Python активно используют Python 3, а разработчики Python в Twitter громогласно заъявляют что миграция на Python 3 идет как и планировалось, я испытываю разочарование, так как подробно описывал свой опыт с Python 3 и как от последнего хочется избавиться.

Я не хочу это делать сейчас, но желаю, чтобы команда разработчиков Python 3 чуть больше прислушалась к мнению сообщества. Для 97% из нас, Python 2, уютненький мирок, в котором мы работали годами, а потому довольно болезненно воспринимается ситуация, когда к нам приходят и заъявляют: Python 3 — прекрасен и это не обсуждается. Это и просто не так в свете множества регрессий. Вместе с теми людьми, которые начинают обсуждать Python 2.8 и Stackless Python 2.8 я не знаю что такое провал, если это не он.

Python: Converting string to bytes object

In this post, we will check how to convert a Python string to a bytes object. Bytes objects are immutable sequences of single bytes in the range between o and 255 (inclusive).

Introduction

In this post, we will check how to convert a Python string to a bytes object. Bytes objects are immutable sequences of single bytes [1] in the range between o and 255 (inclusive) [2].

One of the ways of performing this conversion is by using the bytes class constructor, passing as input the string. Additionally, we need to specify the encoding of the text (in string format) as second argument of the constructor [3].

Alternatively, we can obtain an encoded version of a string as a bytes object by calling the encode method [4]. This method also receives as input the encoding of the text as a string, although in opposition to the previously mentioned constructor this parameter is optional and defaults to UTF-8 [4]. You can read more about Python standard encodings here.

This tutorial was tested on Python version 3.6.


The code

We will start by declaring a string, which we will use to convert to bytes using the two procedures mentioned on the introductory section.

Then, using the first approach, we will create a bytes object from the previous string. To do so, we pass the string as first input of the constructor of the bytes class. As second, we need to specify the encoding, which will be utf-8.

We will store the result in a variable and then print its type, so we can confirm that it is indeed a bytes object. To print the type of a variable, we can simply use Python’s type function.

We will also print our bytes object, created from the string.

The result is shown below in figure 1. As can be seen, we obtain an object of class bytes, as expected. Note that although printing the object shows a user friendly textual representation, the data contained in it are actually bytes, as we will see below.

Figure 1 – String to bytes, using the bytes object constructor.

Moving on, we will now use the second mentioned procedure of conversion, which is calling the encode method on the string. As stated in the introductory section, since this method has UTF-8 as the default encoding when no argument is given, then we will not pass any input to it.

We will again print the object returned by this method, to confirm that this is also a bytes object.

The result for this portion of the code can be seen below at figure 2.

Figure 2 – String to bytes, using the string encode method.

In order to check the actual value of each byte of both bytes objects, we can iterate them with a for in loop and print each element.

Note that in Python 3 print is a function which has an argument called end that defaults to “n”, which is appended at the end of the input to print [5]. Thus, if we specify this argument as a space (” “), all the bytes of each object will be printed with a space between them, rather that each one being printed in a newline.

As shown in figure 3, both objects have the same sequence of bytes.

Figure 3 – Sequence of bytes from both objects.

The final source code can be seen below.

Just as a final note, if we try to assign a value to a byte of one of the bytes object (by using the [] operator), then we will get the exception shown in figure 4, due to the fact that the bytes objects are immutable.

Figure 4 – Assignment error on Python’s bytes object.

Python Bytes, Bytearray

Bytes, Bytearray

Python supports a range of types to store sequences. There are six sequence types: strings, byte sequences (bytes objects), byte arrays (bytearray objects), lists, tuples, and range objects.

Strings contain Unicode characters. Their literals are written in single or double quotes : ‘python’, «data». Bytes and bytearray objects contain single bytes – the former is immutable while the latter is a mutable sequence. Bytes objects can be constructed the constructor, bytes(), and from literals; use a b prefix with normal string syntax: b’python’. To construct byte arrays, use the bytearray() function.

Contents

Bytes literals

bytes() and bytearray() functions

Return a new «bytes» object, which is an immutable sequence of small integers in the range 0 

Python: Tips of the Day

Python: What does if __name__ == «__main__»: do?

Whenever the Python interpreter reads a source file, it does two things:

  • it sets a few special variables like __name__, and then
  • it executes all of the code found in the file.

Let’s see how this works and how it relates to your question about the __name__ checks we always see in Python scripts.

Code Sample

Let’s use a slightly different code sample to explore how imports and scripts work. Suppose the following is in a file called foo.py.

Special Variables

When the Python interpeter reads a source file, it first defines a few special variables. In this case, we care about the __name__ variable.

When Your Module Is the Main Program

If you are running your module (the source file) as the main program, e.g.

the interpreter will assign the hard-coded string «__main__» to the __name__ variable, i.e.

When Your Module Is Imported By Another

On the other hand, suppose some other module is the main program and it imports your module. This means there’s a statement like this in the main program, or in some other module the main program imports:

The interpreter will search for your foo.py file (along with searching for a few other variants), and prior to executing that module, it will assign the name «foo» from the import statement to the __name__ variable, i.e.

Executing the Module’s Code

After the special variables are set up, the interpreter executes all the code in the module, one statement at a time. You may want to open another window on the side with the code sample so you can follow along with this explanation.

Always

  1. It prints the string «before import» (without quotes).
  2. It loads the math module and assigns it to a variable called math. This is equivalent to replacing import math with the following (note that __import__ is a low-level function in Python that takes a string and triggers the actual import):
  3. It prints the string «before functionA».
  4. It executes the def block, creating a function object, then assigning that function object to a variable called functionA.
  5. It prints the string «before functionB».
  6. It executes the second def block, creating another function object, then assigning it to a variable called functionB.
  7. It prints the string «before __name__ guard».

Only When Your Module Is the Main Program

If your module is the main program, then it will see that __name__ was indeed set to «__main__» and it calls the two functions, printing the strings «Function A» and «Function B 10.0».

Only When Your Module Is Imported by Another

(instead) If your module is not the main program but was imported by another one, then __name__ will be «foo», not «__main__», and it’ll skip the body of the if statement.

Always

  • It will print the string «after __name__ guard» in both situations.
  • Summary

    In summary, here’s what’d be printed in the two cases:

    Python 3 Unicode and Byte Strings

    A notable difference between Python 2 and Python 3 is that character data is stored using Unicode instead of bytes. It is quite likely that when migrating existing code and writing new code you may be unaware of this change as most string algorithms will work with either type of representation; but you cannot intermix the two.

    If you are working with web service libraries such as urllib (formerly urllib2) and requests, network sockets, binary files, or serial I/O with pySerial you will find that data is now stored as byte strings.

    You are most likely to notice problems when comparing data against string constants. Comparing Unicode strings against byte strings will fail: either raising a TypeError when using an ordered comparison ( , >=) or always returning False with equality/inequality.

    Character Sets … a bit of history

    If you are already familiar with Unicode or have encountered character set mappings you can skip forward to the Python and Unicode section.

    Computers only work with numbers so that in order to handle text each character is assigned a unique number known as its character code. You are almost certainly familiar with ASCII that defines standard codes for 128 characters. ASCII defines 95 visible characters and 33 non-printing ones such as space, linefeed (often called newline), tab, escape and carriage return (generated by the Enter key). It dates back to 1963 and was designed for use in telegraph and serial communications using 7 of the 8 bits available in a byte (the top bit was used as a parity bit for error checking).

    ASCII only defines codes for the English characters and symbols commonly used in the USA. While it has a code for the dollar sign ($ code #24 or 36 decimal) it doesn’t have codes for, amongst others, the GB pound (£), Yen ( ¥ ) or Euro ( € ) currency symbols.

    Once data started being stored on disk or transmitted via error checking network protocols the 8th bit of each byte could be used for mapping an additional 128 characters. However there was a plethora of ways this could be, and was, defined. While an extended (8-bit) ASCII set added in the GBP and Yen symbols it pre-dated the definition of the Euro.

    Multiple definitions for 8-bit code sets were developed after Extended ASCII with the most commonly occurring in the UK being Latin-1 (ISO-8859-1) and Microsoft CP1252. Both encodings mapped GP Pound, and Yen symbols as well as several Western European accented characters. The sets not 100% compatible with each other and neither included the Euro symbol.

    The Euro was eventually defined in ISO-8859-15 which is essentially 8859-1 with some minor changes to characters so many sites may use 8859-15 yet refer to it as 8859-1. The Euro was added to CP1252 version 3.

    All of this just added to the general confusion over character set mappings and there is a list of at least 50 different character encoding standards on Wikipedia.

    Unicode

    By the late 1980’s attempts were made to define a universal character set (Unicode) using two bytes that would uniquely define over 65,000 different characters. This included western, Greek, Cyrillic, Arabic, Coptic and other characters in Europe and the Middle east, plus Asian characters sets including Kanji used in Japan and China.

    The first 256 characters of Unicode are the same as the ISO-8859-1 (Latin-1) characters. The Euro is officially defined as code #20AC.

    Unicode was intended to encode characters widely used in modern languages using 2 bytes. In 1996 Unicode 2 defined extension planes that allowed four byte character codes to support mappings for historic and specialized character sets.

    Unicode 2 defined characters codes D800–DBFF as a high-surrogate code point which must be followed by a second two byte code (the low-surrogate point). Despite requiring four bytes, the two combined together form a three byte code for characters from 10000 to 10FFF (in the current specification). Emojis are a good example of codes requiring the four byte extension points: the happy face is character code 1F642 (encoded as D83DDE42).

    As of this blog post there are over 137,000 Unicode characters. There are also several unofficial Unicode mappings including constructed scripts for languages such as pIqad Klingon.

    Drawbacks to Unicode

    The obvious disadvantage of Unicode is the need to to use two bytes for every character. This increases memory consumption, disk usage, I/O times, and reduces data transmission rates.

    To support a more efficient method of handling western character sets a UTF-8 encoding scheme was defined that uses up to four bytes to store any Unicode character. ASCII codes require a single byte, codes up to 7FF require two bytes (this includes most European, Middle Eastern and Cyrillic characters). Character codes up to FFFF require three bytes and the rest require four. This means the Euro symbol (20AC) requires three bytes while emojis like the happy face (1F642) require four bytes.

    Python and Unicode

    Your first brush with Python Unicode strings may happen when reading a text file and you get an encoding error, or the characters do not display on the screen correctly.

    Python 3 creates a TextIO object when reading text files and this uses a default encoding for mapping bytes in the file into Unicode characters. Under Linux and OSX the default encoding is UTF-8, while Windows assumes CP1252.

    If your text file does not use the default encoding assumed by Python you will need to specify the encoding when you open the file. To read a file encoded using Latin-1 (ISO-8859-1) use ‘latin_1’:

    A full list of supported encodings is on the Python API codecs page.

    All Python 3 string literals are Unicode. Use the lower case u escape sequence with 4 digits (uxxxx) to define codes up to FFFF. To define the Euro symbol use:

    For Unicode values above FFFF use a upper case U with 8 digits (Uxxxxxxxx). The happy face emoji would be:

    If you print this value it will only display the happy face if your output device supports emojis (command prompts and IDEs typically do not).

    If you have used Unicode text in Python 2 you’ll be familiar with Unicode string literals defined using a u prefix to the string literal (u’Hello world!’). This notation is still supported in Python 3 – just no longer required.

    Python and Byte Strings

    If you work with low level data connections such as serial lines or network sockets (which include web connections and Bluetooth) you will find that Python 3 transfers data as byte strings: data type bytes. Similarly if you open a file in binary mode you’ll be working with byte strings.

    Byte strings support most of the methods provided with Unicode strings (data type str). If your code uses string methods, subscripts and slices it is quite likely to continue working with byte strings.

    However methods that take string parameters (such as startswith) will fail if you pass a string literal (constant) because your code will be passing a Unicode string literal to a byte string method. To define a byte string use a b prefix to string literals. For example:

    Byte strings support the usual backslash escape characters and can contain hexadecimal codes as in ‘xA3’ for the GB pound sign (assuming a Latin_1 character set). You can define raw byte strings to disable backslash escape recognition using either br or rb string prefixes.

    Note that the two string representations are incompatible so the following expression is always False:

    There is no support for a format method in byte strings – string formatting is only supported with Unicode strings. If you print out a byte string using a format string you will always see the byte string representation form. For example:

    Encoding and Decoding Strings

    To convert byte strings to Unicode use the str.decode() method which accepts an encoding parameter. The default encoding is UTF-8 for all platforms. We can correct the previous example to:

    If we are working with Windows CP1252 character sets and had read the text from a binary file we would use (data is the bytes string):

    To convert Unicode to a byte string use the bytes.encode() method , again with an optional encoding parameter (default UTF-8.) To write our hello world message to a Windows CP1252 text file we’d use:

    In reality, for this example we’d probably just write the byte strings separately, or perhaps use string concatenation:

    Format Strings

    As a Python 2 programmer, particularly with a C background, you may have used the % operator for formatting output. This is still supported in Python 3 for both Unicode and byte strings. However the format string and any string parameters must be of the same type. To use byte string printf style formatting use:

    But if you haven’t yet moved over to Format Strings (since Python 2.7) you should really do so as they are far more powerful than the printf style. The recommended approach for formatting output is to use the str.format() method . In its simplest form the format string uses braces to represent a replaceable parameter with similar data type and field width conventions to printf:

    Python 3.6 introduced a new Formatted String Literals feature which uses an f prefix on a Unicode string to allow any Python expression within the braces. Often abbreviated to f-strings these avoid the need to call the format() method for simple formatting cases such as this variant on the hello world example:

    In fact any valid Python expression can be using with the braces so it’s certainly possible to call functions and perform arithmetic such as:

    But this probably isn’t a good idea in practice as it is very difficult to identify the expression (random.random()**2) from the formatting (:.2f) within the string literal. Stick to simple variable names as in the first example.

    If you use formatted string literals then an IDE that parses f-strings is indispensable as it will highlight syntax errors and incorrect variable names.

    At the time of writing PyCharm highlights both syntax errors and incorrect variable names. Of the other popular Python IDEs (Atom, Eclipse/pydev, Spyder and VS Code) there is limited support for syntax checking (not always correctly) but no semantic checks for variable names. That may well have changed by the time you read this so make sure your IDE is kept up to date so you get f-string support when it is available.

    Summary

    Python 3 string class (str) stores Unicode strings and a new byte string (bytes) class supports single byte strings. The two are different types so string expressions must use one form or the other. String literals are Unicode unless prefixed with a lower case b.

    Conversion to Unicode requires knowledge of the underlying character set encoding with UTF-8 being the most commonly used, especially on web pages. To convert byte strings to Unicode use the bytes.decode() method and use str.encode() to convert Unicode to a byte string. Both methods allow the character set encoding to be specified as an optional parameter if something other than UTF-8 is required.

    A new formatted string literal (or f-string) allows expressions to be evaluated within the braces of a formatting string providing an alternative approach to using the str.format() method.

    Новые типы двоичных последовательностей во многих отношениях похожи на тип str в Python 2. Главное что нужно знать — это то, что существуют два основных встроенных типа двоичных последовательностей: неизменяемый тип bytes, появившийся в Python 3, и изменяемый тип bytearray, добавленный в Python 2.6.

    Кстати, в Python 2.6 был также введен тип bytes, но лишь как псевдоним типа str, он ведет себя иначе, чем тип bytes в Python 3.

    Каждый элемент bytes или bytearray — целое число от 0 до 255, а не односимвольная строка, как в типе str в Python 2 str.

    Однако срез двоичной последовательности всегда является двоичной последовательностью того же типа, даже если это срез длины.

    Пример №1. Пятибайтовая последовательность в виде bytes и bytearray.

    1. bytes можно получить из str, если известна кодировка.

    2. Каждый элемент — целое число в диапазоне range(256).

    3. Срезы bytes также имеют тип bytes, даже если срез состоит из одного байта

    4. Для типа bytearray не существует литерального синтаксиса: в оболочке объекты этого типа представляются в виде конструктора bytearray(), аргументом которого является литерал типа bytes.

    5. Срез cafe_arr также имеет типа bytearray

    Тот факт, что my_bytes[0] возвращает int, а my_bytes[:1] — объект bytes длины 1, не должен вызывать удивления. Единственный тип последовательности, для которого s[0] == s[:1] — это типа str.

    И хотя на практике этот тип используется сплошь и рядом, его поведение — исключение из правила. Для всех остальных последовательностей s[i] возвращает один элемент, а s[i:i+1] — последовательность, состоящую из единственного элемента s[i].

    Хотя двоичные последовательности — на самом деле, последовательности целых чисел, в их литеральной нотации отражен тот факт, что часто они включают ASCII-текст.

    Поэтому применяются различные способы отображения, зависящие от значения каждого байта.

      Для байтов из диапазона символов ASCII, имеющих графическое начертание — от пробела до

    выводится сам символ ASCII.

  • Для байтов, соответствующих символам табуляции, новой строки, возврата каретки и , выводятся управляющие последовательности t, n, r и \.
  • Для все остальных байтов выводится шестнадцатеричное представление, например, нулевой байт представляется последовательностью x00.
  • Именно поэтому в примере №1 мы видим представление b’cafxc3xa9′. Первые три байта b’caf’ принадлежат диапазону символов ASCII с графическим начертанием, последний — нет.

    Оба типа bytes и bytearray поддерживают все методы типа str кроме тех, что относятся к форматированию(format, format_map), и еще нескольких, прямо зависящих от особенностей Unicode, в том числе casefold, isdecimal, isidentifier, isnumeric, isprintable и encode.

    Это означает, что при работе с двоичными последовательностями мы можем пользоваться знакомыми методами строк, например endswith, replace, strip, translate, upper и десятками других, только аргументы должны иметь тип bytes, а не str.

    К двоичным последовательностям применимы и функции для работы с регулярными выражениями из модуля re, если регулярное выражение откомпилировано из двоичной последовательности, а не из str.

    Оператор % не работает с двоичными последовательностями в версиях от Python 3.0 до 3.4, но, если верить документу PEP 461, то его предполагается поддержать в будущем.

    Оператор % часто используется во многих языках программирования. Один мой знакомый который разрабатывает шаблоны для WordPress как например ThemeForest Grand Conference рассказал что без оператора % пришлось бы изобретать велосипеды каждый день.

    Для двоичных последовательностей существует метод класса, отсутствующий в типе str: fromhex, который строит последовательность, разбирая пары шеснадцатеричных цифр, которые могут быть разделены пробелами, хотя это и не обязательно.

    Оцените статью
    Fobosworld.ru
    Добавить комментарий

    Adblock
    detector