Spec-Zone.ru › Django 1.11

Перенос на Python 3

Django 1.5 — это первая версия Django, поддерживающая Python 3. Один и тот же код работает как на Python 2 (≥ 2.6.5), так и на Python 3 (≥ 3.2) благодаря слою совместимости six.

Данный документ в первую очередь ориентирован на авторов плагиновых приложений, которые хотят поддерживать как Python 2, так и Python 3. Он также описывает рекомендации, применимые к коду Django.

Философия

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

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

Написание совместимого кода намного проще, если вы ориентируетесь на Python ≥ 2.6. Django 1.5 вводит инструменты совместимости, такие как django.utils.six, который является настроенной версией six module. Для удобства в Django 1.4.2 были введены совместимые алиасы. Если ваше приложение использует эти инструменты, оно потребует Django ≥ 1.4.2.

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

Инструменты переноса, предоставляемые Django, вдохновлены этой философией, и это отражено в данном руководстве.

Рекомендации по переносу

Литералы unicode

Этот этап включает:

  • Добавление from __future__ import unicode_literals в начало ваших модулей Python — лучше всего поместить его в каждый модуль, иначе вам придется постоянно проверять начало файлов, чтобы понять, в каком режиме они находятся;
  • Удаление префикса u перед строками unicode;
  • Добавление префикса b перед байтовыми строками.

Систематическое выполнение этих изменений гарантирует обратную совместимость.

Однако приложения Django обычно не нуждаются в байтовых строках, поскольку Django предоставляет программисту только интерфейсы unicode. Python 3 не рекомендует использовать байтовые строки, за исключением двоичных данных или байтовых интерфейсов. Python 2 делает байтовые строки и строки unicode практически взаимозаменяемыми, если они содержат только данные ASCII. Используйте это, чтобы по возможности использовать строки unicode и избегать префиксов b.

Примечание

Префикс u Python 2 является синтаксической ошибкой в Python 3.2, но он снова будет разрешен в Python 3.3 благодаря PEP 414. Таким образом, эта трансформация необязательна, если вы ориентируетесь на Python ≥ 3.3. Тем не менее, она по-прежнему рекомендуется согласно философии «написания кода Python 3».

Обработка строк

Тип unicode Python 2 был переименован в str в Python 3, str() был переименован в bytes, а basestring исчез. six предоставляет инструменты для обработки этих изменений.

Django также содержит несколько классов и функций, связанных со строками, в модулях django.utils.encoding и django.utils.safestring. Их имена использовали слова str, что имеет разный смысл в Python 2 и Python 3, и unicode, которого нет в Python 3. Чтобы избежать двусмысленности и путаницы, эти понятия были переименованы в bytes и text.

Вот изменения имён в django.utils.encoding:

Старое имя Новое имя
smart_str smart_bytes
smart_unicode smart_text
force_unicode force_text

Для обратной совместимости старые имена по-прежнему работают в Python 2. В Python 3, smart_str является псевдонимом для smart_text.

Для дальнейшей совместимости новые имена работают начиная с Django 1.4.2.

Примечание

django.utils.encoding был глубоко переработан в Django 1.5 для обеспечения более согласованного API. Для получения дополнительной информации см. документацию.

django.utils.safestring в основном используется через функции mark_safe() и mark_for_escaping(), которые не изменились. В случае использования внутренних элементов вот список изменений имён:

Старое имя Новое имя
EscapeString EscapeBytes
EscapeUnicode EscapeText
SafeString SafeBytes
SafeUnicode SafeText

Для обратной совместимости старые имена по-прежнему работают в Python 2. В Python 3, EscapeString и SafeString являются псевдонимами для EscapeText и SafeText соответственно.

Для дальнейшей совместимости новые имена работают начиная с Django 1.4.2.

Методы __str__() и __unicode__()

В Python 2 модель объектов определяет методы __str__() и ` __unicode__()`. Если эти методы существуют, они должны возвращать str (байты) и unicode (текст) соответственно.

Оператор print и встроенная функция str вызывают __str__() для определения удобочитаемого представления объекта. Встроенная функция unicode вызывает ` __unicode__()`, если он существует, а в противном случае возвращает __str__() и декодирует результат с использованием кодировки системы. И наоборот, базовый класс Model автоматически выводит __str__() из ` __unicode__()`_ путем кодирования в UTF-8.

В Python 3 существует только __str__(), который должен возвращать str (текст).

(Также можно определить __bytes__(), но в приложениях Django этот метод используется редко, так как они почти никогда не работают с bytes.)

Django предоставляет простой способ определения методов __str__() и ` __unicode__()`, которые работают как в Python 2, так и в Python 3: необходимо определить метод __str__(), возвращающий текст, и применить декоратор python_2_unicode_compatible().

В Python 3 этот декоратор является бесполезным. В Python 2 он определяет соответствующие методы ` __unicode__()`_ и __str__() (при этом заменяя оригинальный метод __str__()). Вот пример:

from __future__ import unicode_literals
from django.utils.encoding import python_2_unicode_compatible

@python_2_unicode_compatible
class MyClass(object):
    def __str__(self):
        return "Instance of my class"

Этот метод наиболее соответствует философии переноса Django.

Для обеспечения обратной совместимости этот декоратор доступен начиная с Django 1.4.2.

Наконец, обратите внимание, что __repr__() должен возвращать str на всех версиях Python.

dict и dict-подобные классы

dict.keys(), dict.items() и dict.values() возвращают списки в Python 2 и итераторы в Python 3. QueryDict и dict-подобные классы, определенные в django.utils.datastructures, ведут себя аналогично в Python 3.

six предоставляет функции совместимости для работы с этим изменением: iterkeys(), iteritems(), и itervalues(). Он также содержит недокументированную iterlists функцию, которая хорошо работает для django.utils.datastructures.MultiValueDict и его подклассов.

HttpRequest и HttpResponse объекты

Согласно PEP 3333:

  • заголовки всегда являются объектами str,
  • входные и выходные потоки всегда являются объектами bytes.

В частности, HttpResponse.content содержит bytes, что может стать проблемой, если вы сравниваете его с str в своих тестах. Лучшее решение — использовать assertContains() и assertNotContains(). Эти методы принимают ответ и строку Unicode в качестве аргументов.

Рекомендации по написанию кода

Следующие рекомендации применяются в исходном коде Django. Они также рекомендуются для сторонних приложений, которые следуют той же стратегии переноса.

Требования к синтаксису

Unicode

В Python 3 все строки по умолчанию рассматриваются как Unicode. Тип unicode из Python 2 называется str в Python 3, а str становится bytes.

Не используйте префикс u перед строковым литералом Unicode, так как это синтаксическая ошибка в Python 3.2. Вы должны префикс байтовые строки с b.

Для обеспечения того же поведения в Python 2 каждый модуль должен импортировать unicode_literals из __future__:

from __future__ import unicode_literals

my_string = "This is an unicode literal"
my_bytestring = b"This is a bytestring"

Если вам нужен строковый литерал байтов в Python 2 и строковый литерал Unicode в Python 3, используйте встроенную функцию str:

str('my string')

В Python 3 нет автоматических преобразований между str и bytes, и модуль codecs стал более строгим. str.encode() всегда возвращает bytes, а bytes.decode всегда возвращает str. Вследствие этого иногда необходимо использовать следующий шаблон:

value = value.encode('ascii', 'ignore').decode('ascii')

Будьте осторожны, если вам нужно индексировать байтовые строки.

Исключения

При обработке исключений используйте ключевое слово as:

try:
    ...
except MyException as exc:
    ...

Этот устаревший синтаксис был удален в Python 3:

try:
    ...
except MyException, exc:    # Don't do that!
    ...

Синтаксис повторного повышения исключения с другим трассировкой также изменился. Используйте six.reraise().

Магические методы

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

Итераторы

class MyIterator(six.Iterator):
    def __iter__(self):
        return self             # implement some logic here

    def __next__(self):
        raise StopIteration     # implement some logic here

Булева оценка

class MyBoolean(object):

    def __bool__(self):
        return True             # implement some logic here

    def __nonzero__(self):      # Python 2 compatibility
        return type(self).__bool__(self)

Деление

class MyDivisible(object):

    def __truediv__(self, other):
        return self / other     # implement some logic here

    def __div__(self, other):   # Python 2 compatibility
        return type(self).__truediv__(self, other)

    def __itruediv__(self, other):
        return self // other    # implement some logic here

    def __idiv__(self, other):  # Python 2 compatibility
        return type(self).__itruediv__(self, other)

Магические методы ищутся в классе, а не в экземпляре, чтобы отразить поведение интерпретатора Python.

Написание совместимого кода с помощью six

six — это каноническая библиотека совместимости для поддержки Python 2 и 3 в одном кодовом основании. Прочитайте ее документацию!

Модуль customized version of six входит в Django начиная с версии 1.4.2. Вы можете импортировать его как django.utils.six.

Ниже приведены наиболее частые изменения, необходимые для написания совместимого кода.

Обработка строк

Типы basestring и unicode были удалены в Python 3, а значение str изменилось. Для проверки этих типов используйте следующие выражения:

isinstance(myvalue, six.string_types)       # replacement for basestring
isinstance(myvalue, six.text_type)          # replacement for unicode
isinstance(myvalue, bytes)                  # replacement for str

Python ≥ 2.6 предоставляет bytes как псевдоним для str, поэтому вам не нужен six.binary_type.

long

Тип long больше не существует в Python 3. 1L — синтаксическая ошибка. Используйте six.integer_types для проверки, является ли значение целым числом или большим целым:

isinstance(myvalue, six.integer_types)      # replacement for (int, long)

xrange

Если вы используете xrange в Python 2, импортируйте six.moves.range и используйте его вместо этого. Вы также можете импортировать six.moves.xrange (он эквивалентен six.moves.range) но первый метод позволяет просто удалить импорт при отмене поддержки Python 2.

Перемещенные модули

Некоторые модули были переименованы в Python 3. Модуль django.utils.six.moves (на основе six.moves module ) предоставляет совместимое расположение для их импорта.

PY2

Если вам нужен другой код в Python 2 и Python 3, проверьте six.PY2:

if six.PY2:
    # compatibility code for Python 2

Это крайнее решение, когда six не предоставляет подходящей функции.

Настроенная версия six в Django

Версия six, включенная в Django (django.utils.six) включает несколько настроек только для внутреннего использования.

© Django Software Foundation and individual contributors
Licensed under the BSD License.
https://docs.djangoproject.com/en/1.11/topics/python3/

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API