Мой
последний пост о портировании на Python 3 получил несколько действительно хороших ответов, и я многому научился по отзывам, которые видел. Я здесь, чтобы кратко изложить несколько дополнительных советов и подсказок, которые мне прислали люди и которые я узнал, работая с другими портами с тех пор. Пожалуйста, оставляйте их в комментариях к блогу или по электронной почте. Или, что еще лучше, пишите о своих впечатлениях в блоге сами, и я буду ссылаться на них отсюда.
Один из больших уроков, которые я пытаюсь принять, — это поддержка Python 3 в чистом коде Python с единой кодовой базой. В частности, я пытаюсь избежать использования
2to3насколько это возможно. Хотя я думаю, что 2to3 является отличным инструментом, который может облегчить начало поддержки как Python 2, так и Python 3 из одной ветви кода, у него есть некоторые недостатки. Самая большая проблема с 2to3 в том, что он медленный; перебор кода Python может занять много времени, что может стать серьезным препятствием для вашей скорости разработки. Другая проблема 2to3 заключается в том, что он не всегда хорошо работает с другими инструментами разработки, такими как
python setup.py test и
virtualenv , и вам иногда приходится писать дополнительные специальные исправления для преобразования, которые 2to3 не обрабатывает.
Первоначально Автор Барри Варшавы
Учитывая, что почти весь код, который я пишу в эти дни, нацелен на Python 2.6 как минимально поддерживаемую версию Python 2, 2to3 может просто не понадобиться. С моим
портом dbus-python на Python 3 и с моими собственными
пакетами flufl я экспериментирую с игнорированием 2to3 и пытаюсь написать одну кодовую базу для всех Python 2.6, 2.7 и 3.2. Мой коллега
Майкл Фурд очень преуспел в этом подходе, вплоть до Python 2.4, поэтому как минимум 2.6 не должно быть проблемой! Расширения C довольно просты, потому что у вас есть препроцессор C, чтобы помочь вам. Но оказывается, что это обычно не так уж сложно в чистом Python. Я сделал это в моем последнем выпуске
flufl.bounceпакет, и намереваюсь устранить 2to3 в других моих пакетах flufl также скоро.
Первое, что я сделал, это добавил
print_function к
импорту __future__ во всех моих модулях. Раньше я только импортировал
unicode_literals и
absolute_import . Но в документах обычно используется много
операторов print , поэтому переключение на
функцию print () явно удаляет одно большое преобразование 2to3. Кроме того , чтобы разучиться десятилетие
печати мышечной памяти заявления
для печати () функция на самом деле довольно хорошая. Таким образом, мой шаблон модуля теперь выглядит следующим образом (без комментария об авторских правах):
from __future__ import absolute_import, print_function, unicode_literals __metaclass__ = type __all__ = [ ]
Говоря о документах, вы действительно хотите, чтобы они имели тот же набор будущих импортов, что и весь ваш другой код. Я расскажу подробнее о том, как мои собственные пакеты настраивают doctest, но сейчас полезно знать, что я создаю
doctest.DocFileSuite для каждого doctest в моем пакете. Все эти комплекты имеют функцию
setup (), и среда тестирования Python будет вызывать их в соответствующее время, передавая
параметр testobj . Этот аргумент имеет
атрибут globs, который служит глобалом модуля для doctest. Все, что вам нужно сделать для включения будущего импорта в ваших документах, это сделать что-то вроде этого:
def setup(testobj): try: testobj.globs['absolute_import'] = absolute_import testobj.globs['print_function'] = print_function testobj.globs['unicode_literals'] = unicode_literals except NameError: pass
Пробное исключение действительно необходимо, только если вы продолжаете использовать 2to3, так как этот инструмент удалит будущие импорты из всех модулей, которые он обрабатывает. Конечно, будущие импорты все еще существуют в Python 3, поскольку будущие импорты никогда не удаляются. Так что, если вы бросите 2to3, вы можете избавиться от попытки, кроме как тоже.
В последнем выпуске flufl.bounce я изменил API, чтобы все обнаруженные адреса электронной почты были явно байтовыми объектами в Python 3 (и 8-битных строках в Python 2). Это вызвало некоторые проблемы с моими doctests, потому что repr объектов байтов Python 3 отличается от repr 8-битных строк в Python 2. Когда вы печатаете объект в Python 2, вы получаете только содержимое строки, но когда вы распечатав их в Python 3, вы получите
префикс b » .
% python Python 2.7.2+ (default, Dec 18 2011, 17:30:39) [GCC 4.6.2] on linux2 Type "help", "copyright", "credits" or "license" for more information. >>> print b'foo' foo >>> % python3 Python 3.2.2+ (default, Dec 19 2011, 12:03:32) [GCC 4.6.2] on linux2 Type "help", "copyright", "credits" or "license" for more information. >>> print(b'foo') b'foo'
Это означает, что ваш doctest не может быть написан так, чтобы легко поддерживать обе версии Python, когда используются байты / 8-битные строки. Я использую следующий помощник, чтобы обойти это:
def print_bytes(obj): if bytes is not str: obj = repr(obj)[2:-1] print(obj)
Помните, что в Python 2
байты являются просто псевдонимом для
str, поэтому этот код вызывается только в Python 3.
Еще одна забавная проблема с байтами / 8-битными строками заключается в том, что в Python 3 байтовые объекты не
имеют метода .format () . Так что, если вы делаете что-то вроде
b’foo {0} ‘. Format (obj), это будет работать в Python 2, но потерпит неудачу в Python 3. Лучшее, что я придумал для этого, это вместо этого использовать конкатенацию, или выполните форматирование с использованием юникодов, а затем закодируйте их в свой байтовый объект (но тогда вы получите дополнительное удовольствие от выбора подходящей кодировки!).
Знаете ли вы, что
модуль re может сканировать юникоды или байты в Python 3? Переключение производится путем передачи либо
байтовшаблон или шаблон
str , а затем передача в соответствующий тип объекта для анализа. Но если вы используете
префикс r » (т. Е. Необработанные строки) для более разумной обработки обратной косой черты, у вас возникает другая проблема, когда вы хотите проанализировать байты. Python не поддерживает
rb » -prefixes, то есть вы можете иметь как строковые литералы, так и байтовые строковые литералы, но не оба. Вы должны отказаться от того или другого, и я обычно опускаюсь на сторону, чтобы бросить необработанные нити и страдать от разрастания обратной косой черты.
Часть кода, который я переносил, использовала
itertools.izip_longest () , но его нет в Python 3. Вместо этого у вас есть
itertools.zip_longest (), Вам придется выполнить условный импорт (то есть попробовать-кроме), чтобы получить правильную версию.
Вы используете
zope.interfaces ? Вам будет интересно узнать, что синтаксис, к которому мы давно привыкли заявлять, что класс реализует интерфейс, не работает в Python 3. Например:
from zope.interface import Interface, implements class MyInterface(Interface): pass class MyClass: implements(MyInterface)
Это связано с тем, что в Python 3 не работает взлом стека, который
реализует () . К счастью, в последней версии zope.interface появился новый декоратор классов, который вы можете использовать вместо этого. Это работает и в Python 2.6 и 2.7, поэтому измените код, чтобы использовать это:
from zope.interface import Interface, implementer class MyInterface(Interface): pass @implementer(MyInterface) class MyClass: pass
Мне все равно нравится использование декораторов классов.
Вот хитрый. Знаете ли вы, что Python 2 предоставляет некоторые кодеки для выполнения интересных преобразований, таких как вращение Caeser (то есть, rot13)? Таким образом, вы можете делать такие вещи, как:
>>> 'foo'.encode('rot-13') 'sbb'
Это не работает в Python 3, потому что, хотя определенные кодеки str-to-str, такие как rot-13, все еще существуют,
интерфейс str.encode () требует, чтобы кодек возвращал объект байтов. Чтобы использовать кодеки str-to-str как в Python 2, так и в Python 3, вам придется открыть капот и использовать API более низкого уровня, получая и вызывая кодек напрямую:
>>> from codecs import getencoder >>> encoder = getencoder('rot-13') >>> rot13string = encoder(mystring)[0]
Вы должны получить нулевой элемент из возвращаемого значения кодера из-за API кодеков. Немного некрасиво, но это работает в обеих версиях Python.
Это все на данный момент. Удачного портирования!
Источник: http://www.wefearchange.org/2012/01/python-3-porting-fun-redux.html