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