【问题标题】:Neat way to port nonpolyglot code from Py2 to Py3将非多语言代码从 Py2 移植到 Py3 的巧妙方法
【发布时间】:2020-10-12 17:31:04
【问题描述】:

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

from itertools import izip_longest

但现在我正在将我的代码库迁移到 Py3(同时与 Py2 兼容)。而在 Py3 中,izip_longest 被重命名为 zip_longest。 Failing to import itertools in Python 3.5.2 推荐了一个解决此问题的答案,即将导入语句更改为以下。

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

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

【问题讨论】:

  • 你可以使用库six吗?它有from six.moves import zip_longest,即py2中的izip_longest和py3中的zip_longest

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


【解决方案1】:

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

from itertools_compat import izip_longest

无论你在哪里需要这个功能。

【讨论】:

  • 您仍然需要更改 20 个模块才能使用此包装器。 2to3 对此有何看法?
  • 2to3 是可扩展的,您可以编写自己的模式和替换,这将是一个微不足道的案例。如果有人有兴趣,我可以发布一个例子,如果你提出一个单独的问题并从这里或其他地方链接我到它
  • @macattack 你能在这里自己创建一个答案吗?
  • @LokeshAgrawal 我认为这个问题有点偏离主题,而且您已经将此标记为已回答,无法获得甜蜜的答案点。更合适的问题是如何从x 自动重构我的代码 -> y...
  • @macattack 我创建了一个新问题stackoverflow.com/q/62525336/7707677。请在那里分享你的答案
【解决方案2】:

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

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

一般来说,我建议更改您的代码,使其看起来像 python3 代码并且仍然可以使用 python2 运行,而不是编写看起来像 python2 代码但也可以使用 python3 运行的代码。 (但一切都取决于您的上下文)

你可能想试试这个包futurehttps://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

【讨论】:

    【解决方案3】:

    我看到你的另一个问题已经结束,所以我会在这里发帖。 所以,有几个文件,命名很重要!

    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)
    

    【讨论】:

      猜你喜欢
      • 2017-09-24
      • 1970-01-01
      • 2020-01-19
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多