移植到Python 3¶
Django 1.5是Django的第一个版本,支持Python 3.由于 six 兼容性层,同样的代码在Python 2(≥2.6.5)和Python 3(≥3.2)上运行。
本文档主要针对希望支持Python 2和3的可插拔应用程序的作者。它还描述了适用于Django代码的指南。
哲学¶
本文档假设您熟悉Python 2和Python 3之间的更改。如果不是,请先阅读 Python的官方移植指南。刷新你对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提供的移植工具受这种理念的启发,并在本指南中得到反映。
移植提示¶
Unicode字面量¶
此步骤包括:
在你的Python模块的顶部添加
from __future__ import unicode_literals
- 最好把它放在每个模块中,否则你会继续检查文件的顶部,看看哪个模式是有效的;在unicode字符串之前删除
u
前缀;在bytestrings之前添加
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的 unicode 类型在Python 3中重命名为 str
,str()
重命名为 bytes
,basestring 消失。 six 提供 工具 来处理这些变化。
Django还在 django.utils.encoding
和 django.utils.safestring
模块中包含几个字符串相关的类和函数。他们的名字使用了单词 str
,这在Python 2和Python 3中并不意味着相同的东西,而 unicode
在Python 3中不存在。为了避免歧义和混乱,这些概念被重命名为 bytes
和 text
。
这里是 django.utils.encoding
中的名称更改:
旧名称 |
新名称 |
---|---|
|
|
|
|
|
|
为了向后兼容性,旧名称仍然适用于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()
函数使用,这没有改变。如果你使用的内部,这里是名称更改:
旧名称 |
新名称 |
---|---|
|
|
|
|
|
|
|
|
为了向后兼容,旧名称仍然适用于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
基类通过编码为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
和 dict
类¶
在Python 2中的 dict.keys()
,dict.items()
和 dict.values()
返回列表以及在Python 3中的迭代器. QueryDict
和 django.utils.datastructures
中定义的类似 dict
的行为在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。来自Python 2的 unicode
类型在Python 3中被称为 str
,str
变成 bytes
。
你不能在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和一个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')
谨慎如果你有 index 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的规范兼容性库。阅读其文档!
customized version of six
与1.4.2版本的Django捆绑在一起。您可以将其导入为 django.utils.six
。
以下是编写兼容代码所需的最常见更改。
字符串处理¶
在Python 3中删除了 basestring
和 unicode
类型,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
¶
如果在Python 2上使用 xrange
,请导入 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
不提供适当的功能时,这是最后的解决方案。
Django自定义版本的 six
¶
与Django(django.utils.six
)绑定的六个版本包括一些仅供内部使用的自定义。