只使用标准库编写 Python 2 和 3 兼容代码的最佳方法

Best way to write Python 2 and 3 compatible code using nothing but the standard library

我正在尝试让我的一些代码 Python 2 和 3 兼容。

目前我正在努力使用 range/xrange 等函数和 dict.items/dict.iteritems 等方法。理想情况下,我希望我的代码能够在 Python 3.x 中使用前者,在 Python 2.x.

中使用后者

在我看来,使用 if/else 是最简单的实现方式:

if py >= 3:
    for item in array.items()
    ...
else:
    for item in array.iteritems()

但是,这样做会导致大量重复且丑陋的代码。是否有更好的方法 仅使用标准库 来做到这一点?我可以在代码开头的某处声明 always 使用 range/dict.items if py >= 3 and xrange/dict.iteritems 如果不是?

是否可以这样做?

if py < 3:
    use xrange as range

我环顾四周,知道有几个库(例如 six o futurize)用于解决此问题。但是,我在 运行 只有 python 2.7 的服务器上工作,并且不允许在其上安装任何额外的库。我有一些 python3 代码我想使用,但我也想只维护一个版本的代码。

import sys

if sys.version_info.major > 2:
    xrange = range

但正如 Wim 暗示的那样,这 基本上是 自己重写 six

尽你所能 seesix 比处理 range 做的更多。只是例如看六源码中的_moved_attributes列表

虽然 Python 带有 "batteries included",但它的标准库不是也不能包罗万象。它也不是没有缺陷。

有时有 更好的 电池,不使用它们是一种浪费。只需比较 urllib2requests。后者更好用

我猜你在这种情况下混淆了 arraydict

如果您出于任何原因被限制使用 3-d 方库,那么为什么不喜欢这个:

def iterate_items(to_iterate):
    if py >= 3:
        return to_iterate.items()
    else:
        return to_iterate.iteritems()

然后使用它:

for item in iterate_items(your_dict):
    ...

我建议在您的项目模块中编写 py2 或 py3,但不要将它们混合在一起并且根本不包括任何类型的 2/3 检查。您的程序的逻辑不必关心它的 python 版本,除非可能是为了避免冲突的内置对象上的函数。

相反,从您自己的兼容层导入 * 以修复框架之间的差异,并使用阴影使其对实际项目的模块透明。

例如,在兼容性模块中,您可以为 range/xrange 编写 Roland Smith 的替换,而在您的其他模块中添加 "from compatibility import *"。这样做,每个模块都可以使用 "xrange",兼容层将管理 2/3 的差异。

不幸的是,它不会解决现有的对象功能,例如dict.iteritems;通常你会猴子修补 dict 方法,但它在内置类型上是不可能的(参见 )。我可以想象一些解决方法:

  • 函数包装器(本质上是 sobolevn 的回答)
  • 完全不要使用 .items() 函数;在键上使用简单循环,然后使用这些键访问字典:
    for key in my_dict:
        value = my_dict[key]
        # rest of code goes here

我使用的简单 "Don't Make Me Think!" 解决方案是使用以下命令启动简单脚本:

#!/usr/bin/env python
# just make sure that Python 3 code runs fine with 2.7+ too ~98% of the time :)
from __future__ import (division, print_function, absolute_import,
                        unicode_literals)
from builtins import int
try:
    from future_builtins import ascii, filter, hex, map, oct, zip
except:
    pass
import sys
if sys.version_info.major > 2:
    xrange = range

(阻止大多数 pep8 linters 不必要地对你大喊大叫的额外提示:将最后 3 行移到上面 try 块的内部和顶部)

但是我用这个的只有的情况基本上是"shell scripts that were too large and hairy so I quickly rewrote them to Python and I just want them to run under both Python 2 and 3 with 0 dependencies"。 请不要在真正的application/library代码中使用它,直到你确切地知道上面所有行的后果是什么,如果它们足以满足您的用例。

此外,在这种情况下 .iteritems 的 "solution" 是 "just don't use it",忽略内存使用优化,只 总是 使用 .items 相反 - 如果这很重要,这意味着你不再编写“0依赖性简单脚本”,所以只需选择 Python 3 并为其编写代码(或者 Python 2 如果你需要假装我们在 2008 年)。

另外,查看这些资源以获得正确的理解:


(注意: 我回答这个已经回答的问题主要是因为接受的答案大致转化为 "you are stupid and this is dumb" 我发现这个 非常粗鲁 对于 SO 答案:无论这个问题多么愚蠢,以及如何 "wrong" 实际回答它,一个问题都值得一个真正的 answer._

import sys
VERSION = float("{}.{}".format(sys.version_info.major, sys.version_info.minor))

通过使用它,我们可以为所需版本编写条件代码。

if VERSION >= 3.5:
     from subprocess import run as _run
else:
     from subprocess import call as _run