Главная
Блог разработчиков phpBB
 
+ 17 предустановленных модов
+ SEO-оптимизация форума
+ авторизация через соц. сети
+ защита от спама

Портирование на питон 3. Работа над ошибками

Anna | 16.06.2014 | нет комментариев
Примечание от переводчика:
Представляю вам, перевод увлекательной статьи Армина Ронахера, автора веб-фреймворков Flask и Werkzeug, шаблонизатора Jinja2 и вообще знаменитого питониста об востребованных техниках и подводных камнях, применяемых им в его планах при добавлении поддержки третьего питона. Маленькая заметка по поводу наименования данной статьи. Оно является отсылкой к статье Армина 2010 года «Портирование на питон 3. Начальство», в которой он описывал подготовку кода для механического портирования через утилиту 2to3. Как показывает практика, сегодня такой подход является скорее антипаттерном, т.к. с одной стороны, качество кода в итоге сходственных операций приметно ухудшается, а помимо того, такой код приметно сложнее поддерживать.

Позже Исключительно болезненного навыка портирования Jinja2 на 3-й питон, мне пришлось оставить план на холостом ходу на некоторое время, т.к. я слишком крепко опасался сломать поддержку питона 3 версии. Подход, тот, что я применял состоял в написании кода для питона 2 версии и перевода с поддержкой 2to3 на 3-й питон во время установки пакета. Самым неприятным побочным результатом стало то, что всякое метаморфоза, которое вы вносите, требует приблизительно минуты на перевод, тем самым убивая скорость ваших итераций. К счастью, оказалось, что если верно указать финальные версии питона, процесс идет ощутимо стремительней.

Томас Волдман из плана MoinMoin начал с запуска Jinja2 через мой python-modernize с верными параметрами, и пришел к цельному коду, тот, что работает под 2.6, 2.7 и 3.3. Путем маленьких приборок мы сумели прийти к славной кодовой базе, которая работает со всеми версиями питона и при этом, в большинстве своем, выглядит, как обыкновенный код на питоне.

Вдохновившись этим итогом, я несколько раз прошерстил код и начал переводить кое-какой иной код, Дабы добавочно поэкспериментировать с объединенной кодовой базой.

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

Выкиньте поддержку 2.5, 3.1 и 3.2

Это один из самых значимых советов. Отказ от поддержки питона 2.5 сегодня больше, чем допустим, от того что осталось не так уж много людей, его использующих. Отказ от 3.1 и 3.2 — достаточно примитивное решение, рассматривая невысокую знаменитость третьего питона. Но какой толк отказываться от поддержки этих версий? Если коротко, то 2.6 и 3.3 содержат огромное число перекрывающего синтаксиса и вероятностей, тот, что разрешают одному и тому же коду типично трудиться в обоих случаях:

  • Совместимые строковые литералы. 2.6 и 3.3 поддерживают идентичный синтаксис для строк. Вы можете применять как 'foo' для нативных типов строк (байтовые строки в 2.x и юникодные строки в 3.x), так иu'foo' для юникодных строк и b'foo' для байтовых строк либо байтовых объектов.
  • Совместимый print синтаксис. В случае, если у вас применяются print‘ы, вы можете добавить from __future__ import print_function и применять print, как функцию, без необходимости применять функцию-обертку и страдать от других несовместимостей.
  • Совместимый синтаксис отлова эксепшенов. В питоне 2.6 возник новейший синтаксис except Exception as e, тот, что применяется в 3.x.
  • Доступны декораторы классов. Они Исключительно пригодны для механического исправления перемещенных интерфейсов, без необходимости оставлять следы на структуре класса. Скажем, они могут подмогнуть механически переименовать наименование способа из next в __next__, либо __str__ в__unicode__ в питоне 2.x.
  • Встроенная функция next() для вызова next либо __next__. Комфортно, т.к. она работает с приблизительно такой же скоростью, как и прямой вызов способа, следственно вам не придется платить продуктивностью в сопоставлении с проверками в рантайме либо добавления собственной функции-обертки.
  • В питоне 2.6 был добавлен новейший тип bytearray с таким же интерфейсом, что и в 3.3. Это благотворно, т.к. в то время как в питоне 2.6 не хватает объекта bytes, у в нем есть встроенный объект, тот, что имея такое же наименование, является синонимом str и ведет себя абсолютно по-иному.
  • В питоне 3.3 опять возникли кодеки перевода из байтов в байты и из строк в строки, которые были поломаны в 3.1 и 3.2. К сожалению, их интерфейсы стали труднее, и не существует алиасов, но это все куда ближе к тому, что было в 2.x, чем прежде. Это исключительно значимо, если вам надобные основанные на потоках данных кодирования. Эта функциональность всецело отсутствовала начиная с 3.0 до 3.3.

Да, модуль six поможет вам продвинуться вперед, но не стоит недооценивать пользу от вероятности видеть чистый код. Я тривиально утратил интерес к поддержке портированной на 3-й питон Jinja2, т.к. меня ужасал ее код. В то время, объединенный код выглядел некрасиво и страдал в плане продуктивности (непрерывныеsix.b('foo') и six.u('foo')), либо же имел низкую скорость итераций 2to3. Сейчас, разобравшись с этим каждому, я вновь получаю наслаждение. Код Jinja2 выглядит дюже чисто, и вам придется поискать, Дабы обнаружить поддержку совместимости питона 2 и 3 версий. Только несколько частей кода делают что-то в жанре if PY2:.

Оставшаяся часть статьи полагает, что вы хотите поддерживать эти версии питона. Также, попытки поддерживать питон версии 2.5 дюже болезненны и я дюже крепко рекомендую вам от них воздержаться. Помощь 3.2 допустима, если вы готовы обернуть все ваши строки в вызовы функции, что лично я бы не рекомендовал делать по причинам эстетики и продуктивности.

Откажитесь от six

Six — достаточно опрятная библиотека и Jinja2 начинала именно с нее. Но в конце концов, если подсчитать, то в six окажется не так уж много нужных пророческой, Дабы запустить порт под 3-й питон. Безоговорочно, six нужен, если вы собираетесь поддерживать питон 2.5, но начиная с 2.6 и огромнее, остается не так уж много причин применять six. В Jinja2 есть модуль _compat, в котором находятся некоторые нужные хелперы. Включая несколько строк не на питоне 3, каждый модуль совместимости содержит менее 80 строк кода.

Это поможет вам избежать задач, когда пользователи ждут иную версию пакета six из-за иной библиотеки либо добавления иной зависимости в ваш план.

Начните с Modernize

Python-modernize — отличная библиотека для того, Дабы начать портирование. Это версия 2to3, которая генерирует код, работающий в обоих версиях питона. Невзирая на то, что в ней хватает багов, а опции по умолчанию не самые оптимальные, она может подмогнуть вам серьезно продвинуться вперед, делая тоскливую работу за вас. При этом, вам все равно придется пробежаться по коду и подчистить некоторые импорты и шероховатости.

Исправьте ваши тесты

Перед тем, как вы начнете делать что-либо еще, пробегитесь по вашим тестам и удостоверитесь, что они до сих пор не утратили толк. Огромное число задач в стандартной библиотеке питона версий 3.0 и 3.1 возникли в итоге ненарочного метаморфозы поведения тестов в итоге портирования.

Напишите модуль совместимости

Выходит, если вы решили отказаться от six, сумеете ли вы прожить без хелперов? Верный результат — нет. Вам все еще необходим маленький модуль совместимости, но он должен быть довольно мал, Дабы вы могли удерживать его в вашем пакете. Вот примитивный пример, как модуль совместимости может выглядеть:

import sys
PY2 = sys.version_info[0] == 2
if not PY2:
    text_type = str
    string_types = (str,)
    unichr = chr
else:
    text_type = unicode
    string_types = (str, unicode)
    unichr = unichr

Код этого модуля будет зависеть от того, как много изменилось для вас. В случае Jinja2, я разместил туда несколько функций. Там, скажем, есть функции ifilterimap и другие схожие функции из itertools, которые стали частью стандартной библиотеки в 3.x (Я использую имена функций из 2.x, Дабы читающему код было ясно, что применение итераторов тут умышленно и не является оплошностью).

Проверяйте для 2.x, а не для 3.x

В какой-то момент, вам придется проверять, выполняется ли код в 2.x либо 3.x версии питона. В этом случае, я бы рекомендовал вам проверять вначале вторую версию, и помещать провnts_to_string = lambda x: x
Основная идея состоит в том, Дабы реализовать способ __str__ и в 2.x, и в 3.x, дозволив ему возвращать юникодные строки (да, это выглядит несколько коряво в 2.x), и декоратор механически переименует его__unicode__ в 2.x, и добавит __str__ тот, что вызывает __unicode__ и кодирует итог его вызова в utf-8. Такой подход был достаточно обширно распространен в последнее время в модулях для 2.x. Так делают скажем Jinja2 либо Django.

Вот пример применения:

@implements_to_string
class User(object):
    def __init__(self, username):
        self.username = username
    def __str__(self):
        return self.username

Метаморфозы в синтаксисе метаклассов

От того что в третьем питоне метаморфозы в синтаксисе определения метаклассов несовместимы со вторым, процесс портирования становится чуточку сложнее. В six есть функция with_metaclass, которая призвана решить эту загвоздку. Она создает пустой класс, тот, что потом виден в дереве наследования. Мне не понравилось такое решение для Jinja2, следственно я изменил его. Внешнее API осталось таким же, но в реализации применяется непостоянный класс, Дабы добавить метакласс. Плюсы от такого решения в том, что вам не необходимо платить продуктивностью за применение его, при этом дерево наследования остается чистым.

Код решения несколько запутан для понимания. Основная идея полагается на вероятность метакласса изменять класс во время создания, что и применяется родительским классом. Мое решение использует метакласс, Дабы удалить своего родителя из дерева наследования при наследовании классов. В конце концов функция создает пустой класс с пустым метаклассом. У метакласса наследованного пустого класса есть конструктор, тот, что инстанциирует новейший класс от положительного родителя и назначает необходимый метакласс (Прим.: не уверен, что все верно перевел — исходники ниже мне кажется, больше речисты). Таким образом пустые класс и метакласс никогда не видны.

Вот как это выглядит:

def with_metaclass(meta, *bases):
    class metaclass(meta):
        __call__ = type.__call__
        __init__ = type.__init__
        def __new__(cls, name, this_bases, d):
            if this_bases is None:
                return type.__new__(cls, name, (), d)
            return meta(name, bases, d)
    return metaclass('temporary_class', None, {})
And here is how you use it:

class BaseForm(object):
    pass

class FormType(type):
    pass

class Form(with_metaclass(FormType, BaseForm)):
    pass

Словари

Одной из разражающих изменений в третьем питоне стали метаморфозы протоколов итераторов словарей. Во втрором питоне у всех словарей были способы: keys()values() и items(), возращавшие списки, иiterkeys()itervalues() и iteritems(), возвращавшие итераторы. В третьем питоне ни одного из них нет. Взамен этого, они были заменены способами, которые возвращают view-объекты.

keys() возвращает view-объект, тот, что ведет себя аналогично неизменяемому множеству, values()возвращает итерируемый контейнер с вероятностью только чтения (но не итератор!) и items() возвращает что-то схожее на неизменяемое уйма. В отличае от обыкновенных сетов, они могут также указывать на изменяемые объекты, в случае чего некоторые способы могут упасть во время работы программы.

Невзирая на то, что огромное число людей упускает тот момент, что view-объекты не являются итераторами, в большинстве случаев вы можете это легко игнорировать. Werkzeug и Django реализуют несколько собственных сходственных словарям объектов, и в обоих случаях решение было легко игнорировать существование view-объектов, и дозволить keys() и его друзьям возвращать итераторы.

На данный момент, это исключительное умное решение с учетом ограничений, которые накладывает интерпретатор питона. Существуют задачи с:

  • Тот факт, что view-объекты не являются итераторами сами по себе обозначает, что вы создаете временные объекты без специальных на то причин.
  • Поведение, схожее на сеты встроенных view-объектов словарей не может быть воспроизведено на чистом питоне, из-за ограничений интерпретатора
  • Реализация view-объектов для 3.x и итераторов для 2.x оз

Источник: programmingmaster.ru

Оставить комментарий
Форум phpBB, русская поддержка форума phpBB
Рейтинг@Mail.ru 2008 - 2017 © BB3x.ru - русская поддержка форума phpBB