Главная страница
Навигация по странице:

  • 20.2. Основные этапы в Python

  • 20.3. Кодирование

  • 20.5. Юникод и файлы

  • 20.7. Упражнения

  • Как устроен Python. Как устроен Python. Харрисон. Харрисон Мэтт


    Скачать 5.41 Mb.
    НазваниеХаррисон Мэтт
    АнкорКак устроен Python
    Дата05.02.2022
    Размер5.41 Mb.
    Формат файлаpdf
    Имя файлаКак устроен Python. Харрисон.pdf
    ТипДокументы
    #352210
    страница15 из 21
    1   ...   11   12   13   14   15   16   17   18   ...   21
    181
    мер, в английском языке. Наличие 128 разных глифов предоставляет достаточно места для символов нижнего регистра, символов верхнего регистра, цифр и знаков препинания.
    Со временем поддержка языков, отличных от английского, стала более распространенной, и кодировки ASCII оказалось недостаточно. В семей- стве Windows до Windows 98 поддерживается кодировка Windows-1252 с поддержкой различных символов с диакритикой и знаков (например, знака евро).
    Все эти схемы кодирования обеспечивают однозначное отображение байтов на символы. Для поддержки китайской, корейской и японской письменности потребуется много более 128 символов. Четырехбайтовая кодировка позволяет поддерживать свыше 4 миллиардов символов. Тем не менее такая универсальность не дается даром. Для большинства людей, использующих только символы из кодировки ASCII, четырехкратное увеличение затрат памяти при таком же объеме данных выглядит колос- сальными потерями памяти.
    Чтобы иметь возможность поддержки всех символов без лишних затрат памяти, пришлось пойти на компромисс — отказаться от кодирования символов последовательностью битов. Вместо этого символы были абстрагированы. Каждый символ отображался на уникальный кодовый
    пункт (обладающий шестнадцатеричным значением и уникальным именем). Затем различные кодировки отображали кодовые пункты на битовые кодировки. Юникод устанавливает соответствие между сим- волом и кодовым пунктом, а не конкретным представлением. В разных контекстах альтернативные представления могут обеспечивать более эффективные характеристики.
    Одна из кодировок, UTF-32, использует 4 байта для хранения инфор- мации о символе. Такое представление удобно для низкоуровневых программистов благодаря тривиальным операциям индексирования.
    С другой стороны, она расходует вчетверо больше памяти, чем ASCII, для кодирования серии букв латинского алфавита.
    Концепция кодировок переменной ширины также помогала бороться с затратами памяти. Одной из таких кодировок является UTF-8, в ко- торой для представления символа используется от 1 до 4 байтов. Кроме того, UTF-8 обладает обратной совместимостью с ASCII. UTF-8 — самая

    182
    Глава 20. Юникод
    Кодовые таблицы Юникода
    Смайлики-эмоджи
    Код
    Имя
    Глиф
    UTF-8
    Кодовый пункт
    Лица
    Смайлики упорядочены по форме рта для упрощения
    поиска символов в кодовой таблице.
    1F60 0
    1F61 1F62 1F63 1F64
    УЛЫБАЮЩЕЕСЯ ЛИЦО
    ‘\N{GRINNING FACE}’
    ‘\U0001f600’
    ‘ ’
    b’\xf0\x9f\x98\x80’,decode(’utf8’)
    1F600 1F600 1F610 1F620 1F630 1F640 1F601 1F602
    → 263A
    1F603
    ЛИЦО С УХМЫЛКОЙ
    СМЕХ ДО СЛЕЗ
    СМЕХ
    довольный смайлик (светлая улыбка)
    Гл иф
    Им я
    Ко довый пу нк т
    Рис. 20.1. Чтение кодовых таблиц на сайте unicode.org. В таблицах перечислены глифы с соответствующими шестнадцатеричными кодовыми пунктами. За этой таблицей следует другая таблица с кодовым пунктом, глифом и названием. Вы можете использовать глиф, имя или кодовый пункт. Если кодовый пункт содержит более 4 цифр, используйте прописную букву U и дополните код слева нулями до
    8 цифр. Если же цифр 4 и меньше, необходимо использовать строчную букву u и дополнять код слева нулями до 4 цифр. Также приведен пример декодирования байтовой строки UTF-8 в соответствующий глиф

    20.2. Основные этапы в Python
    183
    распространенная кодировка в интернете — отлично подходит для коди- рования символов, так как она поддерживается многими приложениями и операционными системами.
    СОВЕТ
    Чтобы узнать предпочтительную кодировку для вашей машины, выполните следующий код (приведен результат для моего 2015 Mac):
    >>> import locale
    >>> locale.getpreferredencoding(False)
    'UTF-8'
    Еще раз проясним: UTF-8 — кодировка байтов кодовых пунктов Юни- кода. Заявить, что UTF-8 и Юникод — одно и то же, в лучшем случае неточность, а в худшем — демонстрация непонимания способа кодиро- вания символов. Более того, само название происходит от слов «Unicode
    Transformation Format — 8 bit», то есть «формат преобразования Юни- кода — 8-разрядный», то есть это формат для Юникода.
    Рассмотрим пример. Символ с именем
    SUPERSCRIPT
    TWO
    определяется в стандарте Юникода как кодовый пункт U+00b2. Его глиф (печатное представление) имеет вид
    ². В ASCII этот символ представить невозмож- но. В Windows-1252 такое представление существует, символ кодируется байтом b2 в шестнадцатеричной записи (это представление совпадает с кодовым пунктом, хотя такое совпадение не гарантируется). В UTF-8 ему соответствует кодировка c2. Аналогичным образом в UTF-16 ис- пользуется кодировка ff fe b2 00. Таким образом, у этого кодового пункта
    Юникода существует несколько разных кодировок.
    20.2. Основные этапы в Python
    В Python можно создавать строки Юникода. Собственно, вы делали это с первых страниц книги: напомним, что в Python 3 все строки кодируются в Юникоде. Но что делать, если вы хотите создать строку с символами, не входящими в ASCII? Ниже приведен код создания строки «x
    2
    ». Если вы найдете глиф, который должен использоваться в строке, скопируйте символ в код:
    >>> result = 'x²'

    184
    Глава 20. Юникод
    Кодирование и декодирование Юникода
    Строка Юникода "x "
    "x\u00b2"
    "x\N{SUPERSCRIPT TWO}"
    Байтовая строка
    .encode('utf-8')
    .decode('utf-8')
    2
    b"x\xcb\xb2
    Рис. 20.2. Кодирование (в данном случае с использованием UTF-8) строки
    Юникода в байтовое представление и последующее декодирование той же байтовой строки в Юникод (тоже с использованием UTF-8). Также при декодировании следует предельно четко выражать свои намерения, потому что неточность приведет к появлению ошибочных данных или искажению символов
    Такой способ обычно работает, хотя могут возникнуть проблемы, если ваш шрифт не поддерживает указанный глиф. Тогда вместо него будет отображаться так называемый «тофу» (пустой прямоугольник или ромб с вопросительным знаком). Также для включения символов, не входящих в ASCII, можно включить шестнадцатеричный кодовый пункт Юникода после префикса
    \u
    :
    >>> result2 = 'x\u00b2'
    Обратите внимание: эта строка тождественна предыдущей строке:
    >>> result == result2
    True
    Наконец, можно использовать в строке имя кодового пункта, заключив его в фигурные скобки в конструкции
    \N{}
    :
    >>> result3 = 'x\N{SUPERSCRIPT TWO}'
    Все эти способы работают. И все они возвращают одну и ту же строку
    Юникода:
    >>> print(result, result2, result3)
    x² x² x²

    20.3. Кодирование
    185
    Третий вариант получается менее компактным. Но если вы не помните нужный кодовый пункт или глиф не поддерживается шрифтом, пожалуй, этот вариант оказывается самым удобочитаемым.
    СОВЕТ
    В документации Python имеется раздел, посвященный Юникоду. Введите help()
    , а затем
    UNICODE
    . В этом разделе обсуждается не столько Юникод, сколько различные способы создания строк Python.
    20.3. Кодирование
    Пожалуй, один из ключей к пониманию Юникода в Python — понима- ние того, что строка Юникода кодируется в байтовую строку. Байто- вые строки никогда не кодируются, но могут декодироваться в строку
    Юникода. Аналогичным образом строки Юникода не декодируются.
    Также на процессы кодирования и декодирования можно взглянуть под другим углом: кодирование преобразует понятное или осмысленное для человека представление в абстрактное представление, предназначенное для хранения (Юникод в байты или буквы в байты), а декодирование преобразует это абстрактное представление обратно в форму, удобную для человека.
    Для заданной строки Юникода можно вызвать метод
    .encode
    , чтобы про- смотреть ее представление в различных кодировках. Начнем с UTF-8:
    >>> x_sq = 'x\u00b2'
    >>> x_sq.encode('utf-8')
    b'x\xc2\xb2'
    Если Python не поддерживает кодировку, будет выдана ошибка
    UnicodeEncodeError
    . Это означает, что кодовые пункты Юникода не поддерживаются в этой кодировке. Например, ASCII не поддерживает символ возведения в квадрат:
    >>> x_sq.encode('ascii')
    Traceback (most recent call last):
    UnicodeEncodeError: 'ascii' codec can't encode character
    '\xb2' in position 1: ordinal not in range(128)

    186
    Глава 20. Юникод
    Если вы достаточно давно работаете с Python, вероятно, вы сталкивались с этой ошибкой. Она означает, что указанное кодирование не позволит представить все символы. Если вы уверены в том, что хотите заставить
    Python провести кодирование, то можете передать параметр errors
    . Что- бы проигнорировать символы, которые Python не может представить, передайте параметр errors='ignore'
    :
    >>> x_sq.encode('ascii', errors='ignore')
    b'x'
    Если передать параметр errors='replace'
    , Python вставит вопроситель- ные знаки вместо неподдерживаемых байтов:
    >>> x_sq.encode('ascii', errors='replace')
    b'x?'
    ПРИМЕЧАНИЕ
    Модуль encodings содержит отображения для кодировок, поддерживаемых
    Python. В Python 3.6 поддерживаются 99 кодировок. Многие современные приложения стараются ограничиваться UTF-8, но если вам нужна поддерж- ка других кодировок, вполне возможно, что они поддерживаются в Python.
    Таблица кодировок находится в encodings.aliases.aliases
    . Кроме того, таблица кодировок приведена в документации модуля на сайте Python
    1
    Несколько возможных вариантов кодирования для этой строки:
    >>> x_sq.encode('cp1026') # Турецкий b'\xa7\xea'
    >>> x_sq.encode('cp949') # Корейский b'x\xa9\xf7'
    >>> x_sq.encode('shift_jis_2004') # Японский b'x\x85K'
    Хотя Python поддерживает много кодировок, они все реже используются на практике. Как правило, они встречаются только в унаследованных приложениях. В наши дни большинство приложений использует UTF-8.
    1
    https://docs.python.org/3/library/codecs.html

    20.4. Декодирование
    187
    20.4. Декодирование
    Термин «декодирование» имеет особый смысл в Python. Он означает получение последовательности байтов и создание строки Юникода.
    В Python байты никогда не кодируются, а только декодируются (соб- ственно, метода
    .encode для байтов не существует). Если вы запомните это правило, оно избавит вас от многих проблем при работе с символами, не входящими в ASCII. Предположим, у вас имеется последовательность байтов UTF-8 для x
    ²:
    >>> utf8_bytes = b'x\xc2\xb2'
    Работая с символьными данными, представленными в виде байтов, постарайтесь как можно быстрее преобразовать их в строку Юникода.
    Обычно в приложении вам придется иметь дело только со строками, а байты использовать только как механизм сериализации (то есть при сохранении файлов или их передаче по сети). Если вы получите эти байты из какого-то источника и используемый фреймворк или библиотека не преобразует их в строку, можно поступить так:
    >>> text = utf8_bytes.decode('utf-8')
    >>> text
    'x²'
    Другой момент, на который следует обратить внимание, — вы не сможете угадать, в каком виде была закодирована последовательность байтов.
    Вы знаете, что некая последовательность в коде закодирована в UTF-8, потому что вы только что сами ее создали; но если неопознанные байты были получены вами из другого источника, они вполне могут иметь другую кодировку.
    ПРИМЕЧАНИЕ
    Вы должны знать кодировку символов; в противном случае вы можете попытаться провести декодирование с неверной кодировкой и получить неправильные данные. Такие неверно декодированные строки называются
    «модзибакэ» (японское слово, означающее «искажение символов», — но
    «модзибакэ» звучит круче).

    188
    Глава 20. Юникод
    Несколько примеров ошибочного декодирования:
    >>> b'x\xc2\xb2'.decode('cp1026') # Турецкая 'ÌB¥'
    >>> b'x\xc2\xb2'.decode('cp949') # Корейская 'x '
    >>> b'x\xc2\xb2'.decode('shift_jis_2004') # Японская 'x '
    Некоторые кодировки поддерживают не все последовательности байтов.
    Если вы проведете декодирование с ошибочной кодировкой, то вместо искаженных символов вы получите исключение. Например, ASCII не поддерживает следующую последовательность байтов:
    >>> b'x\xc2\xb2'.decode('ascii')
    Traceback (most recent call last):
    UnicodeDecodeError: 'ascii' codec can't decode byte 0xc2
    in position 1: ordinal not in range(128)
    Ошибка
    UnicodeDecodeError означает, что вы пытались преобразовать байтовую строку в строку Юникода и кодировка не позволила создать строки Юникода для всех байтов. Как правило, это указывает на исполь- зование неправильной кодировки. Для получения оптимальных резуль- татов попробуйте определить и использовать правильную кодировку.
    Если кодировка неизвестна, можно передать параметр errors='ignore'
    , но это приведет к потере данных. Эта мера должна использоваться только в самом крайнем случае:
    >>> b'x\xc2\xb2'.decode('ascii', errors='ignore')
    'x'
    20.5. Юникод и файлы
    При чтении текстового файла Python возвращает строки Юникода.
    Python использует кодировку по умолчанию (
    locale.getpre fer re- dencoding(False)
    ). Если вы хотите закодировать текстовый файл в другой кодировке, передайте эту информацию с параметром encoding функ- ции open

    20.5. Юникод и файлы
    189
    Кроме того, кодировку можно задать при открытии файла для записи.
    Не забывайте, что кодировки следует рассматривать как формат сериа- лизации (используемый для передачи информации по интернету или со- хранения данных в файле). Ниже приведены два примера записи в файл.
    С первым файлом кодировка не определяется, поэтому по умолчанию используется UTF-8 (кодировка по умолчанию для системы). Во втором примере в параметре encoding назначается кодировка CP949 (корейская):
    >>> with open('/tmp/sq.utf8', 'w') as fout:
    ... fout.write('x²')
    >>> with open('/tmp/sq.cp949', 'w', encoding='cp949') as fout:
    ... fout.write('x²')
    Просмотрите файлы в терминале UNIX. Вы увидите, что они имеют разное содержимое, потому что используют разные кодировки:
    $ hexdump /tmp/sq.utf8 0000000 78 c2 b2 0000003
    $ hexdump /tmp/sq.cp949 0000000 78 a9 f7 0000003
    Данные можно прочитать из файлов:
    >>> data = open('/tmp/sq.utf8').read()
    >>> data
    'x²'
    Помните, что в Python лучше явно выражать намерения, чем подразуме-
    вать. При работе с кодировками необходима конкретность. При чтении файла Python в системе с UTF-8 попытается декодировать из UTF-8. Это нормально, если файл кодировался в UTF-8, но если нет — вы получите либо модзибакэ, либо ошибку:
    >>> data = open('/tmp/sq.cp949').read()
    Traceback (most recent call last):
    UnicodeDecodeError: 'utf-8' codec can't decode byte
    0xa9 in position 1: invalid start byte

    190
    Глава 20. Юникод
    Если вы явно выразите свои намерения и сообщите Python, что файл был закодирован в CP949, то получите из файла правильные данные:
    >>> data = open('/tmp/sq.cp949', encoding='cp949').read()
    >>> data
    'x²'
    Если вы работаете с текстовыми файлами, содержащими символы, не входящие в ASCII, обязательно явно укажите их кодировку.
    20.6. Итоги
    Юникод определяет соответствие между кодовыми пунктами и глифа- ми. В языке Python строки могут содержать глифы Юникода. Строку
    Юникода можно закодировать в байтовую строку с применением разных кодировок. К строкам Python никогда не применяется декодирование.
    При чтении текстового файла можно задать кодировку, чтобы полу- ченная строка Юникода содержала правильные символы. При записи в текстовый файл можно задать параметр encoding
    , чтобы явно задать используемую кодировку.
    UTF-8 — самая популярная из всех современных кодировок. Если только у вас нет веских причин для использования другой кодировки, исполь- зуйте принятую по умолчанию кодировку UTF-8.
    20.7. Упражнения
    1. Посетите сайт http://unicode.org и загрузите таблицу с кодовыми пун- ктами. Выберите символ, не входящий в набор символов ASCII, и напишите код Python для вывода этого символа как по кодовому пункту, так и по имени.
    2. Существуют различные символы Юникода, которые выглядят как перевернутые версии ASCII-символов. Найдите таблицу отобра- жения этих символов (это будет несложно). Напишите функцию, которая получает строку ASCII-символов и возвращает переверну- тую версию этой строки.

    20.7. Упражнения
    191
    3. Запишите в файл свое имя, записанное перевернутыми символами.
    Приведите примеры кодировок, которые поддерживали бы такую запись вашего имени. Приведите примеры кодировок, которые та- кую запись не поддерживают.
    4. Умные (или закругленные) кавычки не поддерживаются в ASCII.
    Напишите функцию, которая получает строку ASCII и возвращает строку, в которой двойные кавычки заменяются умными кавычка- ми. Например, строка Python comes with ”batteries included” должна превратиться в Python comes with “batteries included” (если присмо- треться повнимательнее, вы увидите, что открывающие кавычки закруглены не так, как закрывающие).
    5. Напишите функцию, которая получает текст со смайликами в ста- ром стиле (
    :)
    ,
    :P
    и т. д.) Используя таблицу эмодзи
    1
    , добавьте в свою функцию код для замены текстовых смайликов их Юникод- версиями из таблицы.
    1
    http://unicode.org/emoji/charts/full-emoji-list.html

    21
    Классы
    Строки, словари, файлы и целые числа — все это объекты. Даже функции представляют собой объекты. В Python почти все является объектом. Есть и исключения: ключевые слова (например, in
    ) объектами не являются.
    Кроме того, имена переменных объектами не являются, но указывают на них. В этой главе мы углубимся в тему и разберемся, что же собой представляет объект.
    Термин «объект» многозначен. Одно из определений объектно-ориен- тированного программирования подразумевает использование структур для группировки данных (состояния) и методов (функций для изменения состояния). Многие объектно-ориентированные языки, такие как C++,
    Java и Python, используют классы для определения состояния, которое может храниться в объекте, и методов для изменения этого состояния.
    Если классы являются определениями состояния и методов, экземпляры являются воплощениями этих классов. Как правило, когда разработчик говорит об объектах, он имеет в виду экземпляры классов.
    В языке Python str
    — имя класса, используемого для хранения строк.
    Класс str определяет методы строк.
    Чтобы создать экземпляр класса str с именем b
    , используйте синтаксис строковых литералов Python:
    >>> b = "I'm a string"
    >>> b
    "I'm a string"

    Классы
    1   ...   11   12   13   14   15   16   17   18   ...   21


    написать администратору сайта