27

我刚刚开始一个新的 Python 项目,理想情况下,我想从一开始就提供 Python 2 和 3 支持,并且开发开销最小。我的问题是,对于全新的项目,最好的方法是什么?

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

还可以选择尝试编写多语言 Python 2/Python 3 代码。尽管这似乎是一个可怕的想法,但我注意到我最近倾向于编写比 Python 3 代码更惯用的代码,即使我仍然将它作为 Python 2 运行。我觉得这只会帮助我自己的过渡,当日子终于到来了,但并没有为提供或至少帮助双重支持做太多事情。

我看到的大多数提供双重支持的项目都在后期添加了 Python 3 支持,所以我特别好奇是否有更好的方法更适合新项目,您可以从中受益。

谢谢!

4

5 回答 5

7

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

如果它是一个库或非常独立的应用程序,一个常见的选择是在 Python 2.7 中开发,尽可能避免在 Python 3.x 中弃用的结构,并诉诸自动化测试来识别 py2to3 留下的需要手动修复的漏洞。

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

对于 unicode 处理,我发现这个PyCon 2012 视频非常有用。该建议对 Python 2.x 和 3.x 都有好处:将来自外部的每个字符串视为字节,并尽快转换为 unicode,并尽可能晚地输出转换为字节的字符串。还有另一个关于日期/时间处理的内容丰富的视频。

[更新]

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

于 2012-07-07T06:02:09.397 回答
5

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

即使对于非常大的包,一个compat.py允许其他代码不变地工作的模块也可能很短:这是我和我的同事帮助制作 2/3 polyglot的包中的一个示例。pikaPika 是那些内部将 unicode 和 8 位字符串混合在一起的项目之一,但现在我们已经在 Python 3 的生产环境中使用它超过 6 个月了,没有出现任何问题。


另一个重要的事情是在开发时始终使用以下__future__s:

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 上更接近一分钟。

于 2016-03-12T15:49:15.563 回答
2

选择 2 或 3,无论是你最喜欢的风格,并通过单元测试让它在其中工作得很好。然后确保这些测试在通过 py2to3 或 py3to2 运行后正常工作。最好维护一个版本的代码。

于 2012-07-07T05:08:20.707 回答
1

我个人的经验是,编写在 Python 2 和 3 中都能正常工作的代码比依赖 2to3/3to2 脚本更容易,因为后者通常无法完全正确地翻译。

也许我的情况很不寻常,因为我正在使用字节类型做很多事情,而 2to3 在转换这些类型方面有一项艰巨的任务,但是拥有一个代码库的便利性超过了在代码中进行一些 hack 的讨厌。

作为一个具体的例子,我的bitstring模块是早期转换为 Python 3 的,相同的代码用于 Python 2.6/2.7/3.x。源代码超过 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 中的相同代码。这条路线并不适合所有人,但它是可以考虑的选项。

于 2012-07-07T18:19:12.287 回答
1

如果您需要对 Python 2.5 或更早版本的支持,使用 Distribute 及其 2to3 集成通常是最好的方法。但是如果你只需要支持 Python 2.6 或更高版本,我会让代码在 Python 2 和 Python 3 下运行而不需要转换。我也会使用六个库来简化此操作。

于 2012-07-08T20:17:18.030 回答