Наши партнеры

UnixForum





Библиотека сайта rus-linux.net

Переход на Python 3

Оригинал: Transitioning to Python 3
Автор: Reuven M. Lerner
Дата публикации: 6 декабря 2016 г.
Перевод: А.Панин
Дата перевода: 12 декабря 2016 г.

Язык программирования Python не является новым, но продолжает развиваться и расширять аудиторию пользователей благодаря тому, что он практически не изменялся с момента представления общественности первой версии интерпретатора. Я не хочу сказать, что язык программирования Python не претерпел никаких изменений; конечно же, этот язык развивался, получая новые функции и оптимизации производительности, причем на сегодняшний день он является актуальным в ряде различных областей начиная с обработки потоков данных и заканчивая автоматизацией тестов и обучением. При этом те, кто использовал Python 15 или 20 лет назад чаще всего рассматривают его новейшую версию не иначе, как результат процесса расширения функционала и эволюции уже хорошо известного им языка программирования.

В то же время, изменения рассматриваемого языка программирования, а в особенности, те из них, которые были внесены в версию Python 3.x, не позволяют исполнять программы, разработанные то время, когда актуальной версией данного языка программирования была версия Python 2, без модификаций с помощью интерпретатора Python 3. Это известная проблема, которая была неизбежна и четко обозначена великодушным пожизненным диктатором сообщества разработчиков языка программирования Python Гвидо ван Россумом еще несколько лет назад в момент начала работы над проектом "Python 3000". Гвидо ожидал, что различным организациям потребуется некоторое время для перехода с Python 2 на Python 3, но при этом он был убежден, что изменения языка программирования все же были необходимы.

Хорошая новость заключается в том, что версия языка программирования Python 3 (на момент написания статьи реализованная в рамках интерпретатора версии 3.5) все же лучше версии Python 2. Плохая же новость заключается в том, что на данный момент все еще существует большое количество компаний (включая многие компании, проводящие тренинги и консультирующие клиентов), которые используют версию Python 2.

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

К тому же, компаниям все же придется осуществлять описанный переход в ближайшем будущем, так как в 2020 году поддержка интерпретатора Python 2 будет полностью прекращена. Это риск, который наверняка не будет оправданным для большинства из них.

Если вам также необходимо осуществить данный переход, но вы не имеете такой возможности, вы автоматически попадаете в группу риска. Однако, существует и еще один вариант действий: вы можете обновлять кодовую базу своих приложений постепенно, модифицируя по 1-2 файлам каждую неделю таким образом, чтобы они успешно обрабатывались как интерпретатором Python 2, так и интерпретатором Python 3. По прошествии нескольких месяцев в результате выполнения описанных инкрементальных изменений вы сможете полностью перейти к использованию интерпретатора Python 3 с относительно малыми затратами времени и ресурсов.

Но как сделать код совместимым с обоими версиями интерпретатора? В рамках данной статьи я приведу ряд советов, которыми вы сможете воспользоваться в процессе модификации кода ваших приложений, вооружившись информацией об изменениях, внесенных в версию языка программирования Python 3, а также инструментами, разработанными специальной для этих целей. Не откладывайте эти изменения до 2019 года; если вы являетесь Python-разработчиком, вы уже должны были задумываться (в середине 2016 года) о том, как сделать свой код максимально совместимым с Python 3.

Что изменилось?

Первый ваш вопрос наверняка будет звучать следующим образом: "Что именно изменилось в Python 3?". Кроме него вполне закономерен и следующий вопрос: "Как мне модифицировать программы для Python 2 таким образом, чтобы они продолжали успешно исполняться с помощью интерпретатора Python 2, но также исполнялись без модификаций с помощью интерпретатора Python 3?". Второй вопрос, скорее всего, является самым важным для моих клиентов и, вероятно, для вашего бизнеса в процессе осуществления перехода.

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

Однако, это вовсе не означает, что в него не было внесено никаких изменений и вы можете не модифицировать код своих программ.

Например, вы практически наверняка никогда не использовали встроенную функцию input при работе с Python 2 для получения пользовательского ввода. Вместо нее чаще всего применялась встроенная функция raw_input. По этой причине в Python 3 нет аналога функции input из Python 2; функция input из Python 3 является полным аналогом функции raw_input из Python 2.

Более радикальным изменением является методика обработки строк интерпретатором. Строки больше не состоят из байт; теперь они состоят из символов Unicode, причем используется кодировка UTF-8. Если 100% строк, обрабатываемых с помощью вашей программы, являются строками в кодировке ASCII, вам очень повезло; по сути, в этом случае вам не придется ничего менять. Но если вы используете строки с символами, не входящими в таблицу символов ASCII, причем вы обрабатываете их в той же программе, в которой осуществляется работа с бинарными файлами, вам придется осуществлять модификации кода программы. Это объясняется тем, что класс str из Python 2 был заменен на класс bytes, а класс unicode из Python 2 - на класс str.

Также в язык программирования Python было внесено множество других изменений, призванных сделать его более производительным. Например, в Python 2 имелись функции range (возвращающая список целочисленных значений) и xrange (возвращающая итератор). Функция range из Python 3 является полным аналогом функции xrange из Python 2, так как последняя является гораздо более производительной и весомых причин для использования старой функции range вместо нее слишком мало. Но если в вашей программе предусмотрено использование списка, возвращаемого старой функцией range, вам придется импровизировать при переходе на Python 3.

Другая проблема, которая потеряла свою актуальность в последние год или два, связана с использованием сторонних библиотек. Если вы используете библиотеки из репозитория PyPI, вам придется проверять не только совместимость вашего кода, но и всех используемых в нем библиотек с интерпретатором Python 3. На мой взгляд, в течение долгого времени именно эти библиотеки были причиной, по которой разработчики не решались модифицировать свой код. Но на сегодняшний день большинство популярных пакетов поддерживает интерпретатор Python 3, что отражено на веб-сайте Python 3 Readiness, созданном для отслеживания соответствующей информации.

Идентификация проблем

Но все же, как модифицировать программу для Python 2 таким образом, чтобы она корректно исполнялась как с помощью интерпретатора Python 2, так и с помощью интерпретатора Python 3? Вы можете исследовать код программы строка за строкой и попытаться обнаружить вызовы фнукций, которые следует модифицировать, но не стоит забывать об инструментах, которые значительно упрощают данный процесс.

Первым таким инструментом является давно известная Python-разработчикам программа pylint, которая обычно используется для проверки стиля и корректности кода на языке Python. Современные версии pylint поддерживают параметр py3k, позволяющий также проверить совместимость вашего кода с интерпретатором Python 3. Например, предположим, что вы написали (ужасную) программу, код которой приведен в Листинге 1. Но как выяснить, какие из ее фрагментов не будут работать? Для этой цели вы можете воспользоваться следующей командой:

pylint --py3k oldstuff.py

В результате должен быть получен следующий вывод:

Этот вывод содержит сообщения об ошибках ("E") и предупреждения ("W"). Из него видно, что print в данной программе выступает в роли оператора, а не как функции. Также функция range используется не для итерации. Кроме того, в программе задействована функция raw_input. Что делать с этой информацией и как улучшить положение вещей? Программа pylint не сообщит вам об этом; в общем-то, это и не ее задача. Но даже список проблем, которые следует исправить для запуска программы с помощью интерпретатора Python 3 - это лучше, чем ничего.

Листинг 1. oldstuff.py

Если вы разработали пакет Python с файлом требований, вы можете загрузить и установить пакет caniusepython3 из репозитория PyPI. Запуск сценария caniusepython3 по отношению к файлу требований позволяет получить список работающих и не работающих компонентов. Если вы не хотите загружать и устанавливать данный пакет, вы можете просто перейти на веб-сайт Can I Use Python 3 и загрузить ваш файл требований на него.

Исправление проблем

В составе интерпретатора Python в течение некоторого времени поставляется программа 2to3, которая анализирует код, совместимый с интерпретатором Python 2, и пытается предложить модификации для достижения его совместимости с интерпретатором Python 3. Вы можете испытать ее, выполнив следующую команду:

2to3 oldstuff.py

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

К счастью, в репозитории PyPI имеется пакет с именем futurize, который не только использует программу 2to3, но и подставляет в код программы директивы импорта, необходимые для достижения совместимости с обоими версиями интерпретатора. Вы можете просто выполнить команду:

futurize oldstuff.py

В результате вы получите вывод в формате утилиты diff (как и в случае программы 2to3), который может использоваться для создания файла, совместимого с обоими версиями интерпретатора или для простого изучения необходимых изменений.

Но что делать в том случае, если у вас есть код, совместимый с интерпретатором Python 3 и вам нужно модифицировать его для достижения обратной совместимости с Python 2? Те же люди, которые разработали пакет futurize, создали еще один пакет с забавным именем pasteurize, который позволяет добавить необходимые директивы импорта в код вашей программы.

Как убедиться в том, что ваш код действительно корректно работает как с интерпретатором Python 2, так и с интерпретатором Python 3 после внесения в код вашей программы изменений, предложенных программой futurize? Вы не сможете проверить это в автоматическом режиме, кроме того, не стоит сомневаться в том, что описанные инструменты будут работать некорректно в определенных ситуациях. По этой причине (а также некоторым другим причинам) крайне важно иметь в наличии полноценный набор тестов с хорошим покрытием кода, совместимого с интерпретатором Python 2. Впоследствии вы сможете использовать этот же набор тестов для проверки совместимости вашего кода с интерпретатором Python 3, чтобы убедиться в том, что он будет корректно работать и с данной версией интерпретатора. Без набора тестов вы не сможете достичь уверенности в том, что обновление кода прошло успешно; даже 100% покрытие кода программы тестами не является гарантией, но позволяет убедиться в том, что вероятность его некорректной работы была минимизирована.

Но что, если вы используете серьезные и нетривиальные механизмы Python 2, которые не обрабатываются программой 2to3 и которые достаточно сложно заменить на эквиваленты вручную? Отличный пакет из репозитория PyPI под названием six позволяет заменить такие конструкции на их на эквиваленты, совместимые с интерпретаторами Python 2 и Python 3. Например, предположим, что вам нужно создать новый объект для работы с текстом, который будет совместим с обоими упомянутыми версиями интерпретатора. В Python 3 для этой цели должен использоваться тип unicode, в Python 2 - str. Разумеется, вам едва ли захочется постоянно использовать операторы if в вашем коде. Поэтому вы можете просто использовать для этой цели модуль six следующим образом:

import six
s = six.text_type()

Теперь вы можете быть уверены в том, что s является объектом нужного типа.

В рамках модуля six объявлен полезный массив, содержащий данные изменений в вашем коде, которые вам может понадобиться отслеживать. Хотите проверить что-либо в пространстве имен builtins (или __builtins__ в Python 2)? Хотите повторно сгенерировать исключения? Хотите использовать модуль StringIO (или BytesIO)? Хотите поработать с метаклассами? Благодаря six вы можете использовать единственную строку кода, которая автоматически будет развернута в множество условных инструкций if, позволяющих достичь совместимости с используемой версией интерпретатора Python.

Даже если вы не будете использовать модуль six в вашем коде, я рекомендую ознакомиться с его документацией хотя бы для того, чтобы быть в курсе изменений, внесенных в Python 3. Она позволит вам получить информацию о неизвестных подробностях (как было в моем случае) всех изменений, которые были внесены в интерпретатор, но не обсуждались в сообществе разработчиков Python2/3, причем вы можете использовать эту информацию для разработки кода, совместимого с обоими версиями интерпретатора.

Заключение

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

Дополнительные ресурсы

О различиях между Python 2 и Python 3 уже написано достаточно много. Отличная подборка информации размещена на веб-сайте Python-Future. Помимо пакетов futurize и pasteurize, данный веб-сайт содержит большое количество документации, в которой описываются различия между версиями интерпретаторов, техники адаптации сценариев и нюансы, на которые следует обращать внимание.

Документация пакета six размещена на данном веб-сайте. Даже если вы не будете использовать пакет six для достижения совместимости сценариев с упомянутыми версиями интерпретаторов, я настоятельно рекомендую ознакомиться с его возможностями.

Наконец, если вы являетесь веб-разработчиком и используете фреймворк Django, вам определенно стоит ознакомиться с документацией проекта Django, относящейся к процедуре перехода на Python 3.

Это особенно важно по той причине, что вы сможете ознакомиться с кодом функций Django для обработки строк, байтов, а также строк Unicode, имена которых незначительно менялись в последние годы. На самом деле, в комплекте поставки Django находится копия библиотеки six, слегка модифицированная для решения задач фреймворка.