我正在对运行在 CPython 2.[567]、CPython 3.[0123] 上的大约 5000 行重复数据删除备份程序 (http://stromberg.dnsalias.org/~strombrg/backshift/) 进行最后润色(3.3 仍然是 alpha 0)、Pypy 1.7 和 Jython 主干。我也尝试过 IronPython,但它是完全不同的东西——它没有标准库,所以没有 backshift 的爱。哦,它可以使用 Cython 作为其最内层的循环,或者 psyco - 但 pypy 比这两者都快,尤其是在 32 位系统上。
无论如何,我发现要编写在 2.x 和 3.x 上同样运行良好的代码,我所需要做的就是:
1) print(variable) 在 2.x 和 3.x 上的工作方式相同。 print(variable1, variable2) 没有。在 2.x 中,print(variable) 表示“评估这个带括号的表达式,并使用 print 语句打印单个结果”。在 3.x 中, print(variable) 表示“在这个单一结果上调用 print 函数。所以 print('abc %d %d' % (1, 2)) 在两者中都可以正常工作,因为它是一个单值结果,并且都使用 % 运算符进行字符串格式化。
2) 避免使用八进制常量。不要写 0755,而是写 (7*64 + 5*8 + 5)。
3) 要在其中一个中执行二进制 I/O,我使用了我的 bufsock 模块。 http://stromberg.dnsalias.org/~strombrg/bufsock.html 我会 os.open 一个文件,并用 bufsock 包装它(或使用模块中的 rawio 类)。在 2.x 上,这将返回编码为 8 位字符串的字节字符串。在 3.x 上,这将返回一个字节对象,它的行为很像一个小整数列表。然后我只是传递一个或另一个,根据需要使用“isinstance(foo,str)”进行测试以区分两者。我这样做是因为对于备份程序来说,字节就是字节——我不想乱用编码来破坏可靠地保存数据,而且并不是所有的编码都能很好地往返。
4) 做异常时,避免使用“as”关键字。相反,使用 EG:
try:
self.update_timestamp()
except (OSError, IOError):
dummy, utime_extra, dummy = sys.exc_info()
if utime_extra.errno == errno.ENOENT:
5) 一堆模块在从 2.x 到 3.x 的过渡中被重命名。因此,请尝试将其中一个导入到原本为空的模块中,例如:
try:
from anydbm import *
except ImportError:
from dbm import *
...这将单独出现在一个模块中,名称为 EG adbm.py。然后,每当我需要键值存储时,我都会导入 adbm 而不是直接导入 2.x 或 3.x 所需的两种不同的东西。然后我会 pylint 除了那个粗短的模块 adbm.py 之外的所有东西 - 以及 pylint 不喜欢的东西。我们的想法是对所有可能的东西进行 pylint,除了“一切都需要 pylint”规则之外,只在一个小模块中单独使用,每个模块一个例外。
6) 设置在 2.x 和 3.x 上运行的自动单元测试和系统测试,然后在至少一个 2.x 解释器和至少一个 3.x 上频繁测试,这很有帮助口译员。我也经常对我的代码运行 pylint,尽管只是一个检查 2.5.x 合规性的 pylint - 我在 pylint 获得 3.x 支持之前启动了该项目。
7) 我设置了一个小的“python2x3”模块,其中包含一些常量和可调用函数以使生活更轻松:http://stromberg.dnsalias.org/svn/python2x3/trunk/python2x3.py
8) b'' 字面量在 2.5 中不起作用,尽管它们在 2.[67] 中起作用。我没有尝试预处理或其他什么,而是设置了一个 constants_mod.py,其中包含很多通常在 3.x 中是 b'' 文字的东西,并将它们从简单的字符串转换为 2 的任何“字节”类型.x 或 3.x。所以它们在模块导入时被转换一次,而不是在运行时一遍又一遍。如果您的目标是 2.[67]及以上,也许有更好的方法,但是当我开始 Pypy 项目时,它只与 2.5 兼容,而 Jython 仍然如此。
9) 在 2.x 中,长整数有一个 L 后缀。在 3.x 中,所有整数都是长整数。所以我只是尽可能避免使用长整数常量; 2.x 会根据需要将整数提升为 long,所以这对于大多数事情来说似乎都很好。
10) 有一堆 Python 解释器可以用来进行测试,这很有帮助。我建了 2.[567]和 3. [0123]并将它们存放在 /usr/local/cpython-x.y/ 以便于测试。我还将一些 Pypy 和 Jython 放在 /usr/local 中,再次方便测试。拥有一个自动化 CPython 构建的脚本非常有价值。
我相信这些都是我在一个不平凡的项目中获得高度可移植的 python 代码库所需的所有扭曲。我上面写的列表中的一大遗漏是,我没有尝试使用 unicode 对象——其他人可能更有资格评论。
HTH