Spec-Zone.ru › Django 1.9

Перенос на 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, basestring исчез, а str() был переименован в bytes. 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 и 3: необходимо определить метод __str__(), возвращающий текст, и применить декоратор python_2_unicode_compatible().

END_OF_DOCUMENT_MARKER ```

В 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 в одном кодовом решении. Прочитайте её документацию!

В Django с версии 1.4.2 включена библиотека customized version of six. Вы можете импортировать её как 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.9/topics/python3/

Spec-Zone.ru

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