只使用标准库编写 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。
尽你所能 see,six
比处理 range
做的更多。只是例如看六源码中的_moved_attributes
列表
虽然 Python 带有 "batteries included",但它的标准库不是也不能包罗万象。它也不是没有缺陷。
有时有 更好的 电池,不使用它们是一种浪费。只需比较 urllib2
和 requests
。后者更好用。
我猜你在这种情况下混淆了 array
和 dict
。
如果您出于任何原因被限制使用 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 年)。
另外,查看这些资源以获得正确的理解:
- http://python-future.org/compatible_idioms.html
- http://lucumr.pocoo.org/2011/1/22/forwards-compatible-python/
- https://wiki.python.org/moin/PortingToPy3k/BilingualQuickRef
(注意: 我回答这个已经回答的问题主要是因为接受的答案大致转化为 "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
我正在尝试让我的一些代码 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。
尽你所能 see,six
比处理 range
做的更多。只是例如看六源码中的_moved_attributes
列表
虽然 Python 带有 "batteries included",但它的标准库不是也不能包罗万象。它也不是没有缺陷。
有时有 更好的 电池,不使用它们是一种浪费。只需比较 urllib2
和 requests
。后者更好用。
我猜你在这种情况下混淆了 array
和 dict
。
如果您出于任何原因被限制使用 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 年)。
另外,查看这些资源以获得正确的理解:
- http://python-future.org/compatible_idioms.html
- http://lucumr.pocoo.org/2011/1/22/forwards-compatible-python/
- https://wiki.python.org/moin/PortingToPy3k/BilingualQuickRef
(注意: 我回答这个已经回答的问题主要是因为接受的答案大致转化为 "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