第一个支持Python 3的版本是Django 1.5。 感谢six的兼容层,无需对代码做出任何改动,就可以让你的代码同时在Python 2 (≥ 2.6.5)和Python 3 (≥ 3.2)上运行。
本文档主要针对希望支持Python 2和3的可插拔应用程序的作者。 它还描述了适用于Django代码的指南。
本文档假定您熟悉Python 2和Python 3之间的更改。 如果你不是,请先阅读Python’s official porting guide。 刷新你对Python 2和3的unicode处理的知识将有所帮助; 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 2兼容的Python 3代码比相反的更有意义。 这不仅使你的代码更加面向未来,但Python 3的优势(如saner字符串处理)开始迅速发光。 处理Python 2成为向后兼容性的要求,我们作为开发人员来处理这样的约束。
Django提供的移植工具受这种理念的启发,并且贯穿本指南。
此步骤包括:
将其放在每个模块中,否则您将继续检查文件的顶部以查看哪个模式有效;
u
前缀;b
前缀。执行这些更改系统地保证向后兼容性。
然而,Django应用程序通常不需要bytestrings,因为Django只将unicode接口暴露给程序员。 Python 3不鼓励使用bytestrings,二进制数据或面向字节的接口除外。 Python 2使得bytestrings和unicode字符串可以有效地互换,只要它们只包含ASCII数据。 利用这一点,尽可能使用unicode字符串,并避免b
前缀。
注
Python 2的u
前缀是Python 3.2中的一个语法错误,但是由于 PEP 414,它将再次允许在Python 3.3中。 因此,如果您定位Python≥3.3,则此转换是可选的。 它仍然推荐,按照“编写Python 3代码”的哲学。
Python 2’s unicode type was renamed str
in Python 3, str()
was renamed bytes
, and basestring disappeared.
六提供tools来处理这些更改。
Django在django.utils.encoding
和django.utils.safestring
模块中还包含多个与字符串相关的类和函数。 他们的名字使用了unicode
这个词,这在Python 2和Python 3以及str
中并不是相同的,它在Python 3中不存在。 为了避免歧义和混淆,这些概念重命名为text
和bytes
。
以下是django.utils.encoding
中的名称更改:
旧名称 | 新名称 |
---|---|
smart_str |
smart_bytes |
smart_unicode |
smart_text |
force_unicode |
force_text |
为了向后兼容,旧名称仍然适用于Python 2。 在Python 3下,smart_text
是smart_str
的别名。
为了向前兼容性,新名称的工作原理与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下,SafeString
和EscapeText
分别是SafeText
和EscapeString
的别名。
为了向前兼容性,新名称的工作原理与Django 1.4.2相同。
__str__()
和__unicode__()
方法¶在Python 2中,对象模型指定__str__()
和`__unicode __()_ _方法。 如果这些方法存在,它们必须分别返回unicode
(bytes)和str
(text)。
print
语句和str
内置调用__str__()
来确定对象的人类可读表示。 内置的unicode
调用`__unicode __()`_,如果它存在,否则回到__str__()
,并用系统编码解码结果。 相反,Model
基类通过编码为UTF-8,从`__unicode __()`_ _自动导出__str__()
。
在Python 3中,只有__str__()
,它必须返回str
(text)。
(也可以定义__bytes__()
,但Django应用程序对该方法几乎没有用处,因为它们几乎不处理bytes
。)
Django提供了一种简单的方法来定义在Python 2和3上工作的__str__()
和`__unicode __()_ _方法:您必须定义一个__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__()
必须在所有版本的Python上返回str
。
dict
and dict
-like classes¶dict.keys()
,dict.items()
和dict.values()
返回列表在Python 2和迭代器在Python 3。 QueryDict
和在django.utils.datastructures
中定义的dict
six和itervalues()
提供了兼容性函数来解决这个变化:iterkeys()
,iteritems()
它还包含未正式记录的django.utils.datastructures.MultiValueDict
函数,适用于iterlists
及其子类。
HttpRequest
and HttpResponse
objects¶根据 PEP 3333:
str
对象,bytes
对象。具体来说,HttpResponse.content
包含bytes
,如果您在测试中将其与str
进行比较,则可能会出现问题。 首选解决方案是依靠assertContains()
和assertNotContains()
。 这些方法接受响应和unicode字符串作为参数。
以下准则在Django的源代码中强制实施。 它们也被推荐用于遵循相同移植策略的第三方应用程序。
在Python 3中,默认情况下所有字符串都被视为Unicode。 来自Python 2的str
类型在Python 3中称为str
,bytes
变为unicode
。
您不能在unicode字符串字面量之前使用u
前缀,因为它在Python 3.2中是语法错误。 必须使用b
前缀字节字符串。
为了在Python 2中启用相同的行为,每个模块必须从__future__
导入unicode_literals
:
from __future__ import unicode_literals
my_string = "This is an unicode literal"
my_bytestring = b"This is a bytestring"
如果您需要Python 2下的字节字符串字符串和Python 3下的Unicode字符串字符串,请使用str
builtin:
str('my string')
在Python 3中,bytes
和str
之间没有任何自动转换,而且codecs
模块变得更加严格。 str.encode()
始终返回bytes
,bytes.decode
始终返回str
。 因此,有时需要以下模式:
value = value.encode('ascii', 'ignore').decode('ascii')
如果您必须索引bytestrings,请谨慎。
捕获异常时,使用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是在单个代码库中支持Python 2和3的规范兼容性库。 阅读它的文档!
A customized version of six
is bundled with Django
as of version 1.4.2. 您可以将其导入为django.utils.six
。
以下是编写兼容代码所需的最常见更改。
在Python 3中删除了unicode
和str
类型,并改变了basestring
的含义。 要测试这些类型,请使用以下习语:
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提供str
作为bytes
的别名,因此您不需要six.binary_type
。
long
¶long
类型在Python 3中不再存在。 1L
是语法错误。 使用six.integer_types
检查值是整数还是长整型:
isinstance(myvalue, six.integer_types) # replacement for (int, long)
xrange
¶如果在Python 2上使用six.moves.range
,请导入xrange
并使用它。 您还可以导入six.moves.range
(它等同于six.moves.xrange
),但第一种方法允许您在删除对Python 2的支持时。
一些模块在Python 3中重命名。 django.utils.six.moves
模块(基于six.moves module
)提供了兼容的位置以导入它们。
six
¶与Django捆绑在一起的六个版本(django.utils.six
)包含一些仅供内部使用的自定义项。
2017年9月6日