Spec-Zone.ru › Django 1.10

Перенос на 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 сначала. Обновление ваших знаний о обработке юникода в 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 определяют удобочитаемое представление объекта. Встроенный вызов unicode вызывает ` __unicode__()`, если он существует, и в противном случае использует __str__() и декодирует результат с использованием кодировки системы. И наоборот, базовый класс Model автоматически выводит __str__() из ` __unicode__()`_, кодируя в UTF-8.

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

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

Django предоставляет простой способ определения методов __str__() и ` __unicode__()`, которые работают в Python 2 и 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(). Эти методы принимают ответ и строку Юникод в качестве аргументов.

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

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

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

Unicode

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

Не следует использовать префикс u перед строкой Юникод, так как это синтаксическая ошибка в 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 и строка Юникод в 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

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

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

Spec-Zone.ru

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