准备将Python 2.x转换为3.x
大家都知道,Python 3 正在慢慢取代 Python 2.x。虽然要等很多年才能把大部分现有的代码都迁移过去,但我们现在可以在 2.x 版本的代码中做一些事情,让这个转换变得更简单。
当然,看看 Python 3.x 的新特性 会很有帮助,但我们现在能做些什么,让即将到来的转换过程更轻松呢?我特别想知道我们可以在脚本开头加上哪些代码,让早期版本的 Python 更像 3.x,当然其他好的习惯也欢迎分享。
我想到的最明显的代码是:
from __future__ import division
from __future__ import print_function
try:
range = xrange
except NameError:
pass
我想到的最明显的好习惯是使用
"{0} {1}!".format("Hello", "World")
来格式化字符串。
还有其他推荐的代码和好习惯吗?
2 个回答
我正在努力养成一个习惯,就是在我想要整数除法(而不是小数)的时候,使用像 var1//var2
这样的写法。这虽然不是朝着 Python 3 迈出的大步,但至少我不用再回去检查我所有的除法运算了 :)
一个很大的问题是,微小的改动和2to3工具无法很好解决的,就是默认字符串类型从字节(bytes)变成了Unicode。
如果你的代码需要处理编码和字节输入输出,那你就得花很多功夫来正确转换,确保需要是字节的地方仍然是字节,并且在合适的阶段进行解码。你会发现一些字符串方法(特别是format()
)和库调用需要Unicode字符串,所以即使它们实际上只是字节,你可能也需要额外的解码和编码步骤,才能把字符串当作Unicode使用。
这个问题还因为一些Python标准库模块在用2to3转换时,没有认真处理字节、Unicode和编码的问题,导致它们自己也搞错了什么字符串类型是合适的。虽然这个问题在逐步解决,但从Python 3.0到3.2,你会发现像urllib、email和wsgiref这样的包在处理字节编码时会出现混乱和潜在的错误。
你可以通过在每次写字符串时都小心来缓解这个问题。对于任何本质上是字符的内容,使用u''
字符串;对于真正的字节,使用b''
字符串;而''
则用于“默认字符串”类型,当不重要或需要匹配库调用的字符串要求时使用。
不幸的是,b''
语法只在Python 2.6中引入,所以这样做会让早期版本的用户受限。
补充:
这有什么区别呢?
哦,天哪。好吧……
一个字节的值范围是0到255,可以表示一堆二进制数据(比如图像的内容)或一些文本,这种情况下必须选择一种标准来将一组字符映射到这些字节中。大多数这种“编码”标准将正常的“ASCII”字符集映射到字节0到127,所以在Python 2中处理仅包含ASCII的文本时,使用字节字符串通常是安全的。
如果你想在字节字符串中使用任何ASCII集之外的字符,那就麻烦了,因为每种编码将不同的字符映射到剩下的字节值128到255,而大多数编码无法将每个可能的字符都映射到字节。这就是为什么你从一个地区加载文件到另一个地区的Windows应用程序时,所有带重音的或非拉丁字母会变成错误字符,导致无法阅读的混乱(也叫“mojibake”)。
还有“多字节”编码,它们试图通过使用多个字节来存储每个字符,以便在可用空间中放入更多字符。这种编码是为了东亚地区引入的,因为有很多汉字。但还有UTF-8,这是一种设计更好的现代多字节编码,可以容纳每一个字符。
如果你在处理多字节编码的字节字符串——而今天你很可能会这样做,因为UTF-8被广泛使用;实际上,现代应用中不应该使用其他编码——那么你面临的问题不仅仅是跟踪你正在使用的编码。len()
会告诉你字节的长度,而不是字符的长度,如果你开始索引和修改字节,很可能会把一个多字节序列拆成两部分,生成一个无效的序列,搞得一团糟。
因此,从Python 1.6开始,出现了原生的Unicode字符串(用u'something'
表示),其中字符串中的每个单位都是一个字符,而不是字节。你可以对它们使用len()
、切片、替换、正则表达式,它们总是会表现得很合适。对于文本处理任务,它们无疑更好,这就是为什么Python 3将它们设为默认字符串类型(不需要在''
前加u
)。
问题是,很多现有接口,比如非Windows操作系统上的文件名、HTTP或SMTP,主要是基于字节的,有一种单独的方式来指定编码。所以当你处理需要字节的组件时,你必须小心地将你的Unicode字符串正确编码为字节,而在Python 3中,你在某些地方必须明确这样做,而之前不需要。
Unicode字符串在内部实现上每个单位占用“两个字节”的存储空间。你永远看不到这个存储;你不应该把它看作字节。你正在处理的单位在概念上是字符,无论Python选择如何在内存中表示它们。
……顺便提一下:
这并不完全正确。在像Windows这样的“窄构建”Python中,Unicode字符串的每个单位技术上不是字符,而是UTF-16的“代码单位”。对于基本多语言平面中的字符,从0x0000到0xFFFF,你不会注意到任何区别,但如果你使用的是超出这个16位范围的字符,也就是“天文平面”中的字符,你会发现它们需要两个单位而不是一个,并且再次,你在切片时可能会拆分一个字符。
这很糟糕,发生这种情况是因为Windows(以及Java等其他系统)在Unicode超出65,000字符限制之前,就选择了UTF-16作为内存中的存储机制。然而,使用这些扩展字符的情况仍然相对少见,Windows上的用户也习惯于在许多应用程序中出现字符破损,所以这对你来说可能不是关键问题。
在“宽构建”中,Unicode字符串由真正的字符“代码点”单位组成,因此即使是超出BMP的扩展字符也可以一致且轻松地处理。这样做的代价是效率:每个字符串单位在内存中占用四个字节的存储。