Python 2和Python 3双重开发

时间:2012-07-07 04:10:05

标签: python python-3.x python-2to3

我刚刚开始一个新的Python项目,理想情况下我想从一开始就提供Python 2和3支持,只需要很少的开发开销。我的问题是,为全新项目做这件事的最佳方式是什么?

作为安装脚本的一部分,我遇到了运行2to3甚至3to2的项目。这似乎是一种非常常见的方式。但是,似乎有几种不同的方法可以做到这一点。我也遇到了Distribute

还可以尝试编写多语言Python 2 / Python 3代码。即使这看起来像一个可怕的想法,我注意到我最近编写的代码更像是Python 3代码,尽管我仍然以Python 2运行它。我有一种感觉这只会帮助我自己的过渡时这一天终于到来了,并没有为提供或至少帮助双重支持做很多事情。

我提供的大多数提供双重支持的项目后期都添加了Python 3支持,所以我特别好奇,如果有更好的方法更适合新项目,那么你可以获得干净的好处。

谢谢!

6 个答案:

答案 0 :(得分:9)

你应该看看six,这是一个为Python 2和3之间不同的东西提供统一界面的库。

答案 1 :(得分:7)

根据我的经验,这取决于项目的类型。

如果它是一个库或非常自包含的应用程序,在Python 2.7中开发一个常见的选择,尽可能避免在Python 3.x中弃用的构造,并采用自动化测试来识别py2to3留下的洞,你将不得不手动修复。

另一方面,对于现实生活中的应用程序,请准备好不断发现尚未移植到py3k的库(有时是重要的库)。大多数情况下,您将别无选择,只能将库移植到Python 3,所以如果您能负担得起,那就去吧。通常我不能,这就是为什么我不支持Python 3用于这种项目(但是我很难编写在适当的时候更容易移植的代码)。

对于unicode处理,我发现这个PyCon 2012 video非常有用。这个建议适用于Python 2.x和3.x:将来自外部的每个字符串视为字节并尽快转换为unicode,并尽可能晚地输出字符串转换为字节。关于date/time handling的另一个非常翔实的视频。

[更新]

这是一个陈旧的答案。 截至今天(2019年),使用Python 2.x启动项目并没有很好的理由,并且有几个令人信服的理由将旧项目移植到Python 3.7+并放弃对Python 2.x的支持

答案 2 :(得分:5)

根据我的经验,最好不要使用像six 这样的库;相反,对于每个包只有一个compat.py,只需要所需的代码,与Scott Griffiths's approach不同。 six还有尝试支持已经过时的Python版本的负担:事实上,当你接受Pythons< = 2.6和< = 3.2消失时,生活会更容易。在2.7中有一些向后移植的兼容性功能,例如.view*上的dict方法,它们与Python 3上的非前缀版本完全相同;另一方面,Python 3.3再次支持unicode字符串上的u前缀。

即使对于非常实质的软件包,compat.py模块允许其他代码保持不变,也可能很短:here's an example from the pika package我和我的同事帮助制作2/3多语言。 Pika是那些真正搞混合unicode和8位字符串的内部组合的项目之一,但是现在我们已经在Python 3的生产中使用它超过6个月没有问题。

其他重要的事情是在开发时始终使用以下__future__

from __future__ import absolute_import, division, print_function

我建议不要使用unicode_literals,因为在任一平台上都有一些字符串需要属于str类型。如果您不使用 unicode_literals,则可以执行以下操作:

  • b'123'是8位字符串文字
  • '123'在两个平台上都是str类型
  • u'123'是两个平台上正确的unicode文本

在任何情况下,请在安装/包构建时不要执行 2to3;过去曾经使用过的一些软件包 - pip install这些软件包在Python 2上耗费了几秒钟,但在Python 3上却接近于分钟。

答案 3 :(得分:2)

选择2或3,无论哪种是您最喜欢的味道,并通过单元测试使其工作得非常好。然后在通过py2to3或py3to2运行它之后确保这些测试有效。最好维护一个版本的代码。

答案 4 :(得分:1)

我个人的经验是,编写在Python 2和3中都没有改变的代码更容易,而不是依赖于2to3 / 3to2脚本,而这些脚本通常无法完全正确地进行翻译。

也许我的情况很不寻常,因为我在使用字节类型做很多事情而且2to3有一个很难转换它们的任务,但是拥有一个代码库的便利性超过了在代码中进行一些黑客攻击的麻烦。

作为一个具体的例子,我的bitstring模块是早期转换为Python 3,相同的代码用于Python 2.6 / 2.7 / 3.x. The source超过4000行代码,这就是我需要让它适用于不同的主要版本:

# For Python 2.x/ 3.x coexistence
# Yes this is very very hacky.
try:
    xrange
    for i in range(256):
        BYTE_REVERSAL_DICT[i] = chr(int("{0:08b}".format(i)[::-1], 2))
except NameError:
    for i in range(256):
        BYTE_REVERSAL_DICT[i] = bytes([int("{0:08b}".format(i)[::-1], 2)])
    from io import IOBase as file
    xrange = range
    basestring = str

好吧,这不是很好,但这意味着我可以用优秀的Python 2风格编写99%的代码,并且所有的单元测试仍然传递给Python 3中的相同代码。这条路线不适合所有人,但是这是一个可以考虑的选择。

答案 5 :(得分:1)

如果您需要支持Python 2.5或更早版本,使用Distribute并且它的2to3集成通常是最好的方法。但是如果你只需要支持Python 2.6或更高版本,我会在没有转换的情况下使代码在Python 2和Python 3下运行。我还会使用six库来简化这一过程。