将非多语言代码从 Py2 移植到 Py3 的巧妙方法

Neat way to port nonpolyglot code from Py2 to Py3

在我的许多 python 模块中,我都在使用

from itertools import izip_longest

但现在我正在将我的代码库迁移到 Py3(同时兼容 Py2)。在 Py3 中 izip_longest 被重命名为 zip_longest。 中推荐了一种解决此问题的方法,即将导入语句更改为以下内容。

try:
    # Python 3
    from itertools import zip_longest as izip_longest
except ImportError:
    # Python 2
    from itertools import izip_longest 

但是,在 20 多个模块中进行更改对我来说有点奇怪。这样做的巧妙方法是什么?

您可以将确切的代码包装在您自己的模块中,比如 itertools_compat.py,然后只写

from itertools_compat import izip_longest

任何你需要这个功能的地方。

使 python2 代码与 python3 一起工作不是免费的。 并且涉及大量 g运行t 工作。 我认为很难避免这种情况,无论如何您都必须进行适当的测试以确保您的代码适用于两个版本。

我不知道你的代码和你的项目,它的确切内容,这个项目是否应该存活更长时间,或者你是否只是想让它存活更长时间。因此,我不确定在您的情况下什么是最好的。

一般来说,我会建议更改您的代码,使其看起来像 python3 代码,并且仍然 运行s 与 python2 而不是编写看起来像 python2 代码,还有 运行 和 python3。 (但这一切都取决于你的背景)

您可能想试用包 future https://python-future.org/ 它提供编写代码的帮助程序,即 运行 两个版本。

future 还包含一些工具,这些工具会尝试以某种方式自动更改代码,这两个版本更有可能 运行。 根据代码的复杂性,您仍然需要手动执行很多操作(尤其是对于 unicode 问题)

命令被称为 futurize(使代码看起来像 python3 但 运行 与 python2) 或 pasteurize(使代码看起来像 python2 但 运行 也与 python3)

在尝试之前备份所有文件(或使用一些版本控制,如 git)。

简单的用例是futurize --stage1 yourfile.py

有一个有趣的章节/备忘单有很多例子,值得一读https://python-future.org/compatible_idioms.html?highlight=zip

如果您不想使用 future 包,或者遇到这种情况,future 不能很好地处理我会编写一个适配器模块并在您的代码中使用它。

例如 py2py3compat.py:

try: 
    # Python 3 
    from itertools import zip_longest
except ImportError: 
    # Python 2 
    from itertools import izip_longest as zip_longest

yourpyfile.py:

from py2py3compat import zip_longest

并执行全局搜索和替换以将 izip_longest 替换为 zip_longest

我看到你的其他问题已关闭,所以我会 post 在这里。 所以,有几个文件,命名很重要!

fixer.py

#!/usr/bin/env python3
# you'll need 2to3 and python-modernize installed
from __future__ import absolute_import
import sys
from lib2to3.main import main
import libmodernize

sys.path.append(".")
sys.exit(main('fixers'))

fixers/fix_iziplongest.py

from lib2to3 import fixer_base
import libmodernize

class FixIziplongest(fixer_base.BaseFix):
    PATTERN = """
    power< 'izip_longest' trailer< '(' any* ')' > >
    | import_from< 'from' 'itertools' 'import' 'izip_longest' >
    """

    # This function is only called on matches to our pattern, which should
    # be usage of izip_longest, and the itertools import
    def transform(self, node, results):
        # add the new import (doesn't matter if we do this multiple times
        libmodernize.touch_import('itertools_compat', 'zip_longest', node)
        # remove the old import
        if node.type == syms.import_from:
            node.parent.remove()
            return node
        
        # rename to izip_longest
        node.children[0].value = 'zip_longest'
        return node

用法与 2to3 - python ./fixer.py -f iziplongest file_to_fix.py 相同(如果您希望它应用更改,则可以添加更多标志,这只会显示差异) 所以,它的作用是隐藏这个:

from itertools import izip_longest
for x in izip_longest(a, b):
     print(x)

为此:

from itertools_compat import zip_longest
for x in zip_longest(a, b):
     print(x)