pyporting.md
1> **Источник:** https://python-all.ru/3.2/howto/pyporting.html2>3> «Документация Python на русском» – неофициальный перевод официальной документации Python: версии от 2.6 до 3.16, полнотекстовый поиск, английский оригинал рядом с переводом. Эта Markdown-версия страницы предназначена для работы с LLM: вставьте её в ChatGPT, Claude или Cursor.45---67# Перенос кода с Python 2 на Python 389| автор: | Brett Cannon |10| --- | --- |1112Аннотация1314Поскольку Python 3 – будущее Python, а Python 2 всё ещё активно используется, полезно иметь свой проект доступным для обеих основных версий Python. Это руководство призвано помочь выбрать наиболее подходящую стратегию поддержки Python 2 и 3 для вашего проекта, а также объяснить, как её реализовать.1516Если требуется портировать модуль расширения, а не чистый код Python, см. [*Портирование модулей расширения на Python 3*](https://python-all.ru/3.2/howto/cporting.html#cporting-howto).1718## Выбор стратегии1920Когда проект принимает решение о необходимости поддержки обеих версий Python 2 и 3, нужно определиться, как этого добиться. Выбранная стратегия будет зависеть от размера существующей кодовой базы проекта и от желаемой степени расхождения между кодовыми базами Python 2 и Python 3 (например, начав новую версию с Python 3).2122Если ваш проект новый или кодовая база невелика, возможно, стоит рассмотреть написание/перенос [*всего кода для Python 3 и использование 3to2*](https://python-all.ru/3.2/howto/pyporting.html#use-3to2) для переноса кода на Python 2.2324Если вы предпочитаете поддерживать кодовую базу, семантически **и** синтаксически совместимую с Python 2 и 3 одновременно, вы можете писать [*исходный код, совместимый с Python 2/3*](https://python-all.ru/3.2/howto/pyporting.html#use-same-source). Хотя это обычно приводит к несколько неидиоматичному коду, это означает, что вы сохраняете быстрый процесс разработки для себя как разработчика.2526Наконец, у вас есть возможность [*использовать 2to3*](https://python-all.ru/3.2/howto/pyporting.html#use-2to3) для перевода кода Python 2 в код Python 3 (с некоторой ручной помощью). Это может быть реализовано в виде ветвления кода и использования 2to3 для создания ветки Python 3. Вы также можете позволить пользователям выполнять перевод автоматически во время установки, чтобы вам нужно было поддерживать только кодовую базу Python 2.2728Независимо от выбранного подхода, перенос не так сложен и не требует столько времени, как может показаться изначально. Вы также можете решать задачу поэтапно, поскольку значительная часть переноса – это просто обновление кода в соответствии с текущими лучшими практиками совместимости с Python 2/3.2930### Универсальные советы3132Независимо от выбранной стратегии, есть несколько моментов, которые стоит учесть.3334Во-первых, убедитесь, что у вас надёжный набор тестов. Нужно быть уверенным, что всё продолжает работать, как и при поддержке новой минорной версии Python. Это означает, что ваш набор тестов должен быть тщательным и правильно перенесён между Python 2 и 3. Также, скорее всего, вы захотите использовать что-то вроде [tox](https://python-all.ru/3.2/howto/pyporting.html) для автоматизации тестирования между виртуальными машинами Python 2 и Python 3.3536Во-вторых, когда ваш проект получит поддержку Python 3, не забудьте добавить правильный классификатор на [Cheeseshop](https://python-all.ru/3.2/howto/pyporting.html) ([PyPI](https://python-all.ru/3.2/howto/pyporting.html)). Чтобы ваш проект был указан как совместимый с Python 3, он должен иметь [классификатор Python 3](https://python-all.ru/3.2/howto/pyporting.html) (из [http://techspot.zzzeek.org/2011/01/24/zzzeek-s-guide-to-python-3-porting/](https://python-all.ru/3.2/howto/pyporting.html)):3738```python39setup(40 name='Your Library',41 version='1.0',42 classifiers=[43 # убедитесь, что используете :: Python *and* :: Python :: 3, чтобы44 # pypi мог отобразить пакет на странице python 345 'Programming Language :: Python',46 'Programming Language :: Python :: 3'47 ],48 packages=['yourlibrary'],49 # убедитесь, что добавили custom_fixers в MANIFEST.in50 include_package_data=True,51 # ...52)53```5455Это приведёт к тому, что ваш проект появится в [списке пакетов Python 3](https://python-all.ru/3.2/howto/pyporting.html). Вы поймёте, что классификатор установлен правильно, так как при посещении страницы вашего проекта на Cheeseshop в левом верхнем углу страницы будет отображаться логотип Python 3.5657В-третьих, проект [six](https://python-all.ru/3.2/howto/pyporting.html) предоставляет библиотеку, которая помогает сгладить различия между Python 2 и 3. Если вы обнаружите, что есть сложный момент, который постоянно вызывает затруднения при переносе или сопровождении кода, рассмотрите возможность использования решения, совместимого на уровне исходного кода, основанного на six. Если вам нужно создать собственное совместимое с Python 2/3 решение, вы можете использовать `sys.version_info[0] >= 3` в качестве защиты.5859В-четвёртых, прочитайте обо всех подходах. То, что какой-то совет больше подходит для одного подхода, не означает, что он не применим к другим стратегиям.6061В-пятых, откажитесь от поддержки старых версий Python, если возможно. [Python 2.5](https://python-all.ru/3.2/howto/pyporting.html) ввёл много полезного синтаксиса и библиотек, которые стали идиоматическими в Python 3. [Python 2.6](https://python-all.ru/3.2/howto/pyporting.html) ввёл future-операторы, которые значительно упрощают совместимость при переходе с Python 2 на 3. [Python 2.7](https://python-all.ru/3.2/howto/pyporting.html) продолжает эту тенденцию в стандартной библиотеке. Поэтому выберите самую новую версию Python, которую вы можете сделать минимально поддерживаемой, и работайте от неё.6263## Python 3 и 3to26465Если вы начинаете новый проект или ваша кодовая база достаточно мала, возможно, стоит рассмотреть написание кода для Python 3 и обратный перенос на Python 2 с помощью [3to2](https://python-all.ru/3.2/howto/pyporting.html). Благодаря тому, что Python 3 более строг в некоторых вещах, чем Python 2 (например, байты и строки), трансляция исходного кода может быть проще и прямолинейнее, чем с Python 2 на 3. Кроме того, это даёт вам более непосредственный опыт разработки на Python 3, что, поскольку это будущее Python, хорошо в долгосрочной перспективе.6667Недостаток этого подхода в том, что 3to2 – сторонний проект. Это означает, что основные разработчики Python (а значит, и данное руководство) не могут давать никаких гарантий относительно того, насколько хорошо 3to2 работает в любой момент времени. Однако нет оснований полагать, что 3to2 – некачественный проект.6869## Python 2 и 2to37071Включённый в состав Python начиная с версии 2.6, инструмент [2to3](https://python-all.ru/3.2/py3k/library/2to3.html) (и модуль [`lib2to3`](https://python-all.ru/3.2/library/2to3.html#module-lib2to3)) помогает переносить код с Python 2 на Python 3, выполняя различные трансформации исходного кода. Это идеальное решение для проектов, которые хотят ответвить код Python 3 от своей кодовой базы Python 2 и поддерживать их как независимые кодовые базы. Вы можете даже начать подготовку к использованию этого подхода уже сегодня, написав совместимый с будущими версиями код Python, который чисто работает в Python 2 вместе с 2to3; все описанные ниже шаги будут работать с кодом Python 2 до того момента, когда фактически начнётся использование 2to3.7273Использование 2to3 в качестве этапа перевода по требованию во время установки также возможно, что избавляет от необходимости поддерживать отдельную кодовую базу Python 3, но у этого подхода есть недостатки. Хотя пользователям придётся заплатить за перевод только один раз при установке, вам как разработчику придётся нести эти затраты регулярно во время разработки. Если ваша кодовая база достаточно велика, этап перевода превращается в аналог компиляции, лишая вас быстрого процесса разработки, к которому вы привыкли в Python. Очевидно, время, необходимое для перевода проекта, варьируется, поэтому выполните пробный перевод, чтобы оценить, сколько времени это занимает, и решить, предпочитаете ли вы этот подход по сравнению с использованием [*исходного кода, совместимого с Python 2/3*](https://python-all.ru/3.2/howto/pyporting.html#use-same-source) или простым поддержанием отдельной кодовой базы Python 3.7475Ниже приведены типичные шаги, которые предпринимает проект, использующий подход на основе 2to3 для поддержки Python 2 и 3.7677### Поддержка Python 2.77879Первым делом убедитесь, что ваш проект совместим с [Python 2.7](https://python-all.ru/3.2/howto/pyporting.html). Это хорошая практика, поскольку Python 2.7 – последний выпуск Python 2 и поэтому будет использоваться довольно долгое время. Это также позволяет использовать флаг `-3` для Python, чтобы помочь обнаружить места в вашем коде, которые 2to3 не может обработать, но которые, как известно, вызывают проблемы.8081### Старайтесь поддерживать только [Python 2.6](https://python-all.ru/3.2/howto/pyporting.html) и новее8283Хотя это возможно не для всех проектов, если вы можете поддерживать [Python 2.6](https://python-all.ru/3.2/howto/pyporting.html) и **только** более новые версии, ваша жизнь станет намного проще. Различные future-операторы, дополнения стандартной библиотеки и т.д. существуют только в Python 2.6 и новее, что очень помогает при переносе на Python 3. Но если ваш проект должен поддерживать [Python 2.5](https://python-all.ru/3.2/howto/pyporting.html) (или даже [Python 2.4](https://python-all.ru/3.2/howto/pyporting.html)), то перенос на Python 3 всё ещё возможен.8485Ниже перечислены преимущества, которые вы получаете, если вам нужно поддерживать только Python 2.6 и новее. Некоторые из этих вариантов являются личным выбором, в то время как другие **настоятельно** рекомендуются (те, что больше относятся к личному выбору, помечены как таковые). Если вы продолжаете поддерживать более старые версии Python, то вам, по крайней мере, нужно следить за ситуациями, которые эти решения исправляют.8687#### `from __future__ import print_function`8889Это личный выбор. 2to3 довольно хорошо обрабатывает перевод оператора print в функцию print, так что это необязательный шаг. Однако этот future-оператор помогает привыкнуть к набору `print('Hello, World')` вместо `print 'Hello, World'`.9091#### `from __future__ import unicode_literals`9293Ещё один личный выбор. Вы всегда можете пометить то, что должно быть (юникодной) строкой, префиксом `u` для достижения того же эффекта. Но независимо от того, используете вы этот future-оператор или нет, вы **должны** точно знать, какие строки Python 2 вы хотите сделать байтами, а какие – строками. Это означает, что вы должны **как минимум** пометить все строки, предназначенные для текста, префиксом `u`, если вы не используете этот future-оператор.9495#### Байтовые литералы9697Это **очень** важный момент. Возможность помечать строки Python 2, которые должны содержать байты, префиксом `b` помогает чётко разграничить, что является строкой Python 3, а что – нет. Когда запускается 2to3 на коде, все строки Python 2 становятся строками Python 3, **если только** они не помечены префиксом `b`.9899Существуют некоторые различия между байтовыми литералами в Python 2 и Python 3, поскольку в Python 2 тип bytes является просто псевдонимом для `str`. Вероятно, самая большая «ловушка» – это то, что индексация возвращает разные значения. В Python 2 значением `b'py'[1]` является `'y'`, а в Python 3 – `121`. Избежать этого расхождения можно, всегда выполняя срез размером в один элемент: `b'py'[1:2]` даёт `'y'` в Python 2 и `b'y'` в Python 3 (то есть, достаточно близко).100101В Python 3 нельзя конкатенировать байты и строки. Но поскольку в Python 2 bytes является псевдонимом `str`, это сработает: `b'a' + u'b'` работает в Python 2, а `b'a' + 'b'` в Python 3 вызывает [`TypeError`](https://python-all.ru/3.2/library/exceptions.html#TypeError). Аналогичная проблема возникает и при сравнении байтов и строк.102103### Поддержка только [Python 2.5](https://python-all.ru/3.2/howto/pyporting.html) и новее104105Если вы поддерживаете [Python 2.5](https://python-all.ru/3.2/howto/pyporting.html) и новее, всё ещё есть некоторые возможности Python, которые можно использовать.106107#### `from __future__ import absolute_import`108109Неявные относительные импорты (например, импорт `spam.bacon` из модуля `spam.eggs` с помощью инструкции `import bacon`) не работают в Python 3. Это выражение \_\_future\_\_ отходит от такой практики и разрешает использование явных относительных импортов (например, `from . import bacon`).110111В [Python 2.5](https://python-all.ru/3.2/howto/pyporting.html) необходимо использовать инструкцию \_\_future\_\_, чтобы получить возможность использовать явные относительные импорты и предотвратить неявные. В [Python 2.6](https://python-all.ru/3.2/howto/pyporting.html) явные относительные импорты доступны и без этой инструкции, но она всё ещё нужна для предотвращения неявных относительных импортов. В [Python 2.7](https://python-all.ru/3.2/howto/pyporting.html) инструкция \_\_future\_\_ не требуется. Другими словами, если вы не поддерживаете только Python 2.7 или версию старше Python 2.5, используйте инструкцию \_\_future\_\_.112113### Работа с распространёнными «подводными камнями»114115Есть несколько моментов, которые постоянно оказываются камнями преткновения для людей, – их 2to3 не может обработать автоматически, или же они легко выполняются в Python 2, чтобы помочь модернизировать код.116117#### `from __future__ import division`118119Хотя того же результата можно добиться, используя аргумент `-Qnew` Python, использование этого future-оператора снимает требование, чтобы пользователи использовали этот флаг для получения ожидаемого поведения деления в Python 3 (например, `1/2 == 0.5; 1//2 == 0`).120121#### Указывайте при открытии файла в двоичном режиме122123Если вы не работали в Windows, возможно, вы не всегда утруждали себя добавлением режима `b` при открытии двоичного файла (например, `rb` для чтения в двоичном режиме). В Python 3 двоичные файлы и текстовые файлы чётко различаются и взаимно несовместимы; подробнее см. модуль [`io`](https://python-all.ru/3.2/library/io.html#module-io). Поэтому **необходимо** принять решение, будет ли файл использоваться для двоичного доступа (чтение и/или запись байтовых данных) или текстового доступа (чтение и/или запись данных Unicode).124125#### Текстовые файлы126127Текстовые файлы, созданные с помощью `open()` в Python 2, возвращают байтовые строки, тогда как в Python 3 они возвращают строки Unicode. В зависимости от стратегии переноса это может быть проблемой.128129Если требуется, чтобы текстовые файлы возвращали строки Unicode в Python 2, есть две возможности:130131- В Python 2.6 и выше используйте [`io.open()`](https://python-all.ru/3.2/library/io.html#io.open). Поскольку [`io.open()`](https://python-all.ru/3.2/library/io.html#io.open) по сути та же функция и в Python 2, и в Python 3, это поможет устранить любые возможные проблемы.132- Если требуется совместимость с версиями ниже 2.6, тогда используйте [`codecs.open()`](https://python-all.ru/3.2/library/codecs.html#codecs.open). Это гарантирует, что в Python 2 вы получите строки Unicode.133134#### Наследуйте от `object`135136Классы нового стиля существуют с [Python 2.2](https://python-all.ru/3.2/howto/pyporting.html). Необходимо убедиться, что вы наследуете от `object`, чтобы избежать странных крайних случаев с порядком разрешения методов и т.д. Это по-прежнему полностью допустимо в Python 3 (хотя и не нужно, так как все классы неявно наследуют от `object`).137138#### Работа с дихотомией байтов и строк139140Одна из самых больших проблем, с которой сталкиваются люди при переносе кода на Python 3, – это дихотомия байтов и строк. Поскольку в Python 2 тип `str` мог содержать текстовые данные, люди со временем стали довольно небрежно разграничивать, какие экземпляры `str` содержат текст, а какие – байты. В Python 3 уже нельзя быть таким беспечным и нужно правильно обрабатывать различие. Ключ к решению этой проблемы – убедиться, что **каждый** строковый литерал в вашем коде Python 2 синтаксически или функционально помечен как байтовые или текстовые данные. После этого нужно убедиться, что API спроектированы так, чтобы обрабатывать либо конкретный тип, либо быть должным образом полиморфными.141142##### Разметка строковых литералов Python 2143144Первое, что нужно сделать – обозначить каждый строковый литерал в Python 2 как текстовый или байтовый. Если вы поддерживаете только Python 2.6 или новее, это можно сделать, пометив байтовые литералы префиксом `b`, а текстовые – префиксом `u` или с помощью future-оператора `unicode_literals`.145146Если ваш проект поддерживает версии Python до 2.6, тогда следует использовать проект [six](https://python-all.ru/3.2/howto/pyporting.html) и его функцию `b()` для обозначения байтовых литералов. Для текстовых литералов можно использовать функцию `u()` из six или префикс `u`.147148##### Определите, что будут принимать API149150В Python 2 было очень легко случайно создать API, который принимал как байтовые, так и текстовые данные. Но в Python 3, благодаря более строгой обработке разнотипных данных, такое свободное использование байтов и текста вместе обычно приводит к ошибкам.151152Возьмем словарь `{b'a': 'bytes', u'a': 'text'}` в Python 2.6. Он создает словарь `{u'a': 'text'}`, так как `b'a' == u'a'`. Но в Python 3 эквивалентный словарь создает `{b'a': 'bytes', 'a': 'text'}`, т.е. без потери данных. Подобные проблемы могут возникать при переносе кода Python 2 на Python 3.153154Это означает, что нужно выбрать, что API будет принимать и создавать, и последовательно придерживаться этого API в Python 2 и 3.155156##### Сравнение байтов и Unicode157158В Python 3 смешивание байтов и unicode запрещено в большинстве ситуаций; это вызовет [`TypeError`](https://python-all.ru/3.2/library/exceptions.html#TypeError), тогда как Python 2 попытался бы выполнить неявное приведение типов. Однако есть один случай, когда это не так, и это может ввести в заблуждение:159160```python161>>> b"" == ""162False163```164165Это потому, что сравнение на равенство по языку всегда должно завершаться успехом (и возвращать `False` для несовместимых типов). Однако это также означает, что неправильно портированный код на Python 3 может демонстрировать ошибочное поведение, если такие сравнения выполняются незаметно. Для обнаружения таких ситуаций в Python 3 есть флаг `-b`, который будет показывать предупреждение:166167```python168$ python3 -b169>>> b"" == ""170__main__:1: BytesWarning: Comparison between bytes and string171False172```173174Чтобы превратить предупреждение в исключение, используйте флаг `-bb`:175176```python177$ python3 -bb178>>> b"" == ""179Traceback (most recent call last):180 File "<stdin>", line 1, in <module>181BytesWarning: Comparison between bytes and string182```183184#### Индексирование байтовых объектов185186Еще одно потенциально удивительное изменение – это поведение индексирования байтовых объектов в Python 3:187188```python189>>> b"xyz"[0]190120191```192193Действительно, байтовые объекты Python 3 (а также объекты [`bytearray`](https://python-all.ru/3.2/library/functions.html#bytearray)) являются последовательностями целых чисел. Но код, перенесенный из Python 2, часто предполагает, что индексирование байтовой строки дает другую байтовую строку, а не целое число. Чтобы согласовать оба поведения, используйте срезы:194195```python196>>> b"xyz"[0:1]197b'x'198>>> n = 1199>>> b"xyz"[n:n+1]200b'y'201```202203Единственная оставшаяся ловушка в том, что выход за границы при срезе возвращает пустой байтовый объект вместо возбуждения `IndexError`:204205```python206>>> b"xyz"[3]207Traceback (most recent call last):208 File "<stdin>", line 1, in <module>209IndexError: index out of range210>>> b"xyz"[3:4]211b''212```213214#### `__str__()`/`__unicode__()`215216В Python 2 объекты могут определять как строковое, так и unicode представление себя. В Python 3, однако, существует только строковое представление. Это становится проблемой, поскольку люди могут нечаянно делать в своих методах `__str__()` вещи, которые приводят к непредсказуемым результатам (например, бесконечная рекурсия, если использовать идиому `unicode(self).encode('utf8')` в качестве тела метода `__str__()`).217218Есть два способа решить эту проблему. Первый – использовать пользовательский фиксер 2to3. В блоге по адресу [http://lucumr.pocoo.org/2011/1/22/forwards-compatible-python/](https://python-all.ru/3.2/howto/pyporting.html) описывается, как это сделать. Это позволит 2to3 заменить все вхождения `def __unicode(self): ...` на `def __str__(self): ...`. Для этого требуется, чтобы метод `__str__()` был определён в Python 2 перед методом `__unicode__()`.219220Другой вариант – использовать примесь (mixin class). Это позволяет определить только метод `__unicode__()` для вашего класса, а примесь сама сгенерирует `__str__()` (код из [http://lucumr.pocoo.org/2011/1/22/forwards-compatible-python/](https://python-all.ru/3.2/howto/pyporting.html)):221222```python223import sys224225class UnicodeMixin(object):226227 """Миксин-класс для определения правильных методов __str__/__unicode__228 методов в Python 2 или 3.229230 if sys.version_info[0] >= 3: # Python 3231 def __str__(self):232 return self.__unicode__()233 else: # Python 2234 def __str__(self):235 return self.__unicode__().encode('utf8')236237class Spam(UnicodeMixin):238239 def __unicode__(self):240 return u'spam-spam-bacon-spam' # 2to3 удалит префикс 'u'.241```242243#### Не индексируйте исключения244245В Python 2 следующее работало:246247```python248>>> exc = Exception(1, 2, 3)249>>> exc.args[1]2502251>>> exc[1] # Только Python 2!2522253```254255Но в Python 3 индексирование непосредственно исключения является ошибкой. Необходимо убедиться, что индексирование производится только по атрибуту [`BaseException.args`](https://python-all.ru/3.2/library/exceptions.html#BaseException.args), который представляет собой последовательность, содержащую все аргументы, переданные методу [`__init__()`](https://python-all.ru/3.2/reference/datamodel.html#object.__init__).256257Еще лучше использовать документированные атрибуты, которые предоставляет исключение.258259#### Не используйте `__getslice__` и компанию260261Уже некоторое время считалось устаревшим, но Python 3 наконец отказался от поддержки `__getslice__()` и т.д. Полностью переходите на [`__getitem__()`](https://python-all.ru/3.2/reference/datamodel.html#object.__getitem__) и ее друзей.262263#### Обновление doctest-ов264265[2to3](https://python-all.ru/3.2/py3k/library/2to3.html) попытается сгенерировать исправления для доктестов, которые встретит. Однако это не идеально. Если вы написали монолитный набор доктестов (например, одну строку документации, содержащую все доктесты), стоит хотя бы рассмотреть разбивку доктестов на более мелкие части, чтобы их было легче исправлять. В противном случае может быть вполне оправданно потратить время и усилия на перенос тестов на [`unittest`](https://python-all.ru/3.2/library/unittest.html#module-unittest).266267#### Обновление *map* для несбалансированных входных последовательностей268269В Python 2 *map* дополнял входные последовательности неравной длины значениями *None*, возвращая последовательность длиной, равной длине самой длинной входной последовательности.270271В Python 3, если входные последовательности для *map* имеют разную длину, *map* остановится по достижении конца самой короткой последовательности. Для полной совместимости с *map* из Python 2.x оберните последовательности в [`itertools.zip_longest()`](https://python-all.ru/3.2/library/itertools.html#itertools.zip_longest), например: `map(func, *sequences)` превращается в `list(map(func, itertools.zip_longest(*sequences)))`.272273### Устранение `-3` предупреждений274275При запуске набора тестов вашего приложения запускайте его с флагом `-3`, передаваемым Python. Это вызовет появление различных предупреждений во время выполнения о том, что 2to3 не может обработать автоматически (например, удалённые модули). Старайтесь устранять эти предупреждения, чтобы сделать ваш код ещё более переносимым на Python 3.276277### Запуск 2to3278279После того как ваш код Python 2 стал совместимым с Python 3 на будущее, пора использовать [2to3](https://python-all.ru/3.2/py3k/library/2to3.html) для непосредственного переноса кода.280281#### Вручную282283Чтобы вручную преобразовать исходный код с помощью [2to3](https://python-all.ru/3.2/py3k/library/2to3.html), используйте сценарий `2to3`, который устанавливается вместе с Python 2.6 и новее.:284285```python2862to3 <directory or file to convert>287```288289Это заставит 2to3 вывести diff со всеми применёнными фиксерами для преобразованного исходного кода. Если вы хотите, чтобы 2to3 сразу применил изменения, передайте ему флаг `-w`:290291```python2922to3 -w <stuff to convert>293```294295Доступны и другие флаги для управления тем, какие именно фиксеры применяются, и т. д.296297#### Во время установки298299Когда пользователь устанавливает ваш проект для Python 3, можно поручить либо [`distutils`](https://python-all.ru/3.2/library/distutils.html#module-distutils), либо [Distribute](https://python-all.ru/3.2/howto/pyporting.html) запустить [2to3](https://python-all.ru/3.2/py3k/library/2to3.html) от вашего имени. Для distutils используйте следующий шаблон:300301```python302try: # Python 3303 from distutils.command.build_py import build_py_2to3 as build_py304except ImportError: # Python 2305 from distutils.command.build_py import build_py306307setup(cmdclass = {'build_py': build_py},308 # ...309)310```311312Для Distribute:313314```python315setup(use_2to3=True,316 # ...317)318```319320Это позволит вам не распространять отдельную версию проекта для Python 3. Однако это требует, чтобы при разработке вы хотя бы собирали проект и использовали собранный исходный код Python 3 для тестирования.321322### Проверка и тестирование323324К этому моменту ваш проект, вероятно, уже преобразован так, что работает в Python 3. Проверьте это, запустив модульные тесты и убедившись, что ничего не пошло не так. Если что-то упущено, выясните, как исправить это в Python 3, перенесите исправление обратно в код Python 2 и снова прогнать код через 2to3, чтобы убедиться, что исправление преобразуется правильно.325326## Исходный код, совместимый с Python 2/3327328Хотя это может показаться нелогичным, можно писать код Python, который будет совместим на уровне исходного кода между Python 2 и 3. Это приводит к коду, который не является полностью идиоматическим Python (например, необходимость извлекать текущее вызванное исключение из `sys.exc_info()[1]`), но его можно запускать как в Python 2, **так и в** Python 3 без использования [2to3](https://python-all.ru/3.2/py3k/library/2to3.html) в качестве этапа трансляции (хотя этот инструмент следует использовать для поиска потенциальных проблем переносимости). Это позволяет сохранять быстрый процесс разработки независимо от того, разрабатываете ли вы под Python 2 или Python 3. Какой подход – этот или использование [*Python 2 и 2to3*](https://python-all.ru/3.2/howto/pyporting.html#use-2to3) – подходит вам лучше, решается для каждого проекта индивидуально.329330Чтобы получить полное представление о том, с какими проблемами придётся столкнуться, обратитесь к разделу [What’s New in Python 3.0](https://python-all.ru/3.2/release/3.0/whatsnew/3.0.html). Другие люди переорганизовали данные в других форматах, например, [http://docs.pythonsprints.com/python3\_porting/py-porting.html](https://python-all.ru/3.2/howto/pyporting.html).331332Ниже описаны некоторые шаги, которые помогут попытаться поддерживать и Python 2, и 3 из одного исходного кода.333334### Следуйте инструкциям по использованию [2to3](https://python-all.ru/3.2/py3k/library/2to3.html)335336Все шаги, описанные в разделе о том, как [*портировать код Python 2 с помощью 2to3*](https://python-all.ru/3.2/howto/pyporting.html#use-2to3), применимы и к созданию кодовой базы для Python 2/3. Это включает в себя попытку поддерживать только Python 2.6 или новее (инструкции [`__future__`](https://python-all.ru/3.2/library/__future__.html#module-__future__) работают в Python 3 без проблем), устранение предупреждений, возникающих из-за `-3`, и так далее.337338Стоит даже рассмотреть запуск [2to3](https://python-all.ru/3.2/py3k/library/2to3.html) на вашем коде (без фиксации изменений). Это позволит узнать, где в коде находятся потенциально проблемные места, чтобы исправить их должным образом, прежде чем они станут серьёзной проблемой.339340### Используйте [six](https://python-all.ru/3.2/howto/pyporting.html)341342Проект [six](https://python-all.ru/3.2/howto/pyporting.html) содержит множество средств для написания переносимого кода на Python. Обязательно прочитайте его документацию от начала до конца и используйте все предоставляемые им возможности. Так вы минимизируете количество ошибок, которые могут возникнуть при написании кода, совместимого с разными версиями.343344### Перехват текущего возбуждённого исключения345346Одно из изменений между Python 2 и 3, которое потребует изменения подхода к написанию кода (если вы поддерживаете [Python 2.5](https://python-all.ru/3.2/howto/pyporting.html) и более ранние версии), касается доступа к текущему возбуждённому исключению. В Python 2.5 и более ранних версиях синтаксис для доступа к текущему исключению был таким:347348```python349try:350 raise Exception()351except Exception, exc:352 # Текущее исключение: 'exc'353 pass354```355356Этот синтаксис изменился в Python 3 (и был перенесён в [Python 2.6](https://python-all.ru/3.2/howto/pyporting.html) и новее) на:357358```python359try:360 raise Exception()361except Exception as exc:362 # Текущее исключение: 'exc'363 # В Python 3 'exc' ограничен блоком; в Python 2.6 оно будет "утекать"364 pass365```366367Из-за этого изменения синтаксиса нужно перейти на захват текущего исключения следующим образом:368369```python370try:371 raise Exception()372except Exception:373 import sys374 exc = sys.exc_info()[1]375 # Текущее исключение: 'exc'376 pass377```378379Из [`sys.exc_info()`](https://python-all.ru/3.2/library/sys.html#sys.exc_info) можно получить больше информации о возбуждённом исключении, чем просто экземпляр текущего исключения, но, скорее всего, она не нужна.380381> **Примечание**382>383> В Python 3 трассировка прикрепляется к экземпляру исключения через атрибут `__traceback__`. Если экземпляр сохраняется в локальной переменной, которая существует за пределами блока `except`, трассировка создаст циклическую ссылку с текущим фреймом и его словарём локальных переменных. Это задержит освобождение мёртвых ресурсов до следующего прохода циклической [*сборки мусора*](https://python-all.ru/3.2/glossary.html#term-garbage-collection).384>385> В Python 2 эта проблема возникает только в том случае, если сохранить саму трассировку (например, третий элемент кортежа, возвращаемого [`sys.exc_info()`](https://python-all.ru/3.2/library/sys.html#sys.exc_info)) в переменной.386387## Другие ресурсы388389Авторы следующих записей в блогах, страниц вики и книг заслуживают особой благодарности за то, что опубликовали свои советы по портированию кода Python 2 на Python 3 (и тем самым помогли предоставить информацию для этого документа):390391- [http://python3porting.com/](https://python-all.ru/3.2/howto/pyporting.html)392- [http://docs.pythonsprints.com/python3\_porting/py-porting.html](https://python-all.ru/3.2/howto/pyporting.html)393- [http://techspot.zzzeek.org/2011/01/24/zzzeek-s-guide-to-python-3-porting/](https://python-all.ru/3.2/howto/pyporting.html)394- [http://dabeaz.blogspot.com/2011/01/porting-py65-and-my-superboard-to.html](https://python-all.ru/3.2/howto/pyporting.html)395- [http://lucumr.pocoo.org/2011/1/22/forwards-compatible-python/](https://python-all.ru/3.2/howto/pyporting.html)396- [http://lucumr.pocoo.org/2010/2/11/porting-to-python-3-a-guide/](https://python-all.ru/3.2/howto/pyporting.html)397- [http://wiki.python.org/moin/PortingPythonToPy3k](https://python-all.ru/3.2/howto/pyporting.html)398399Если вы считаете, что в этом документе чего-то не хватает и это следует добавить, отправьте письмо в список рассылки [python-porting](https://python-all.ru/3.2/howto/pyporting.html).400