Spec-Zone.ru › Django 1.8

Перенос на 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.)

END_OF_DOCUMENT_MARKER

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(). Эти методы принимают ответ и строку 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.8/topics/python3/

Spec-Zone.ru

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