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