【问题标题】:Create package with backward compatibility创建具有向后兼容性的包
【发布时间】:2019-10-27 09:38:34
【问题描述】:

我正在尝试在 3.6 中创建 python 包,但我也希望向后兼容 2.7 如何为 3.6 编写代码和 2.7

例如,我有一个名为 geo_point() 的方法。

def geo_point(lat: float, lng: float):
    pass

此函数在 3.6 中可以正常工作,但在 2.7 中无法正常工作,它显示语法错误我认为 2.7 不支持类型提示。所以我想编写另一个 2.7 支持的功能,当用户在 2.7 上运行我的包时,它会忽略所有不支持的功能

例如

 @python_version(2.7)
 def geo_point(lat, lng):
     pass

是否可以让函数和python共同决定哪个函数兼容?

【问题讨论】:

  • Python 2 将在 2 个月内结束生命周期。在新项目中仍然支持它的努力会更糟吗?
  • 虽然 python 将在 2020 年 1 月结束生命,但我知道相当多的项目可能会在 python2 中停留更长时间。这当然不是一个明智的选择,但对于庞大的遗留代码,有时别无选择。我仍然同意克劳斯的观点。如果不是真的有必要,那就花时间在这方面。
  • 更正之前的评论:“如果不是真的有必要,那就花时间在这方面的努力上”应该是“如果不是真的必要,那么花不要时间在这上面努力”

标签: python


【解决方案1】:

如果类型提示是您的代码的唯一问题,请查看 SO question Type hinting in Python 2

它说,python3 也尊重注释行中的类型提示。 Python2 将忽略它,而 python3 尊重这种替代语法。它专为仍然必须与 python2 兼容的代码而设计。

但是请注意,仅仅因为代码是用python2编译的,并不意味着它会产生正确的结果。

如果您有更多的兼容性问题,我强烈建议您查看future 模块(不要与from __future__ import xxx 声明混淆。

您可以使用 pip install future 安装 future (https://pypi.org/project/future/)。

由于您没有显示任何其他导致问题的代码,因此我无法就具体问题提出建议。

网址https://python-future.org/compatible_idioms.html 显示了一个相当广泛的潜在 python 2 / python 3 问题列表以及如何解决它们。

例如,对于在 python 2 中打开具有较少编码/unicode 问题的文件,可以通过使用以下行导入 open 的替代版本来完成

from io import open

https://python-future.org/compatible_idioms.html

附录

如果你真的需要为 python3 声明一个函数,为 python2 声明一个函数,你可以这样做:

import sys

if sys.version_info < (3, 0):
    def myfunc(a, b):  # python 2 compatible function
        bla bla bla
else:
    def myfunc(a, b):  # python 3 compatible function
        bla bla bla

但是:对于 python 2 和 python 3,这两个函数必须在语法上是正确的

如果你真的想拥有只有 python2 或只有 python3 语法正确的函数(例如 print 语句或 await),那么你可以这样做:

import sys

if sys.version_info < (3, 0):
    from myfunc_py2 import myfunc  # the file myfunc_py2.py does not have to be python3 compatible
else:
    from myfunc_py3 import myfunc  # the file myfunc_py3.py does not have to be python2 compatible

【讨论】:

  • 我添加了一个部分,解释如何在sixfuture 都不能帮助您编写与版本无关的代码的情况下添加特定于版本的代码。这不应该经常发生,但我在一个代码超过 100.000 行的项目中工作,并且不得不这样做一两次。
【解决方案2】:

我认为 2.7 不支持类型提示

实际上,Python 2 支持类型提示,您可以编写向后兼容的代码。见the answer about Python 2 type hinting on StackOverflow

【讨论】:

    【解决方案3】:

    虽然其他答案强调类型提示,但我认为

    可能会有所帮助。项目基础和文档链接位于https://pypi.org/project/six/

    Six 是一个 Python 2 和 3 兼容库。它提供了用于平滑 Python 版本之间差异的实用函数,目的是编写在两个 Python 版本上兼容的 Python 代码。有关所提供内容的更多信息,请参阅文档。

    Python 2 和 Python 3 之间的显着区别之一是 print 语句中缺少括号会导致 Python 3 中的语法错误,例如,

    print "Hello, World"  # OK in Python 2, syntax error in Python 3.
    

    六个解决了这些差异。

    【讨论】:

    • 我最初使用了六个,同时我更喜欢future (pypi.org/project/future)。两者都能胜任。我的印象是未来会更好一些,但由于这是固执己见,我建议查看sixfuture,然后再选择。这两个包都旨在允许编写可以使用 python2 和 python3 执行的代码,直到您确定不再需要使用 python2 运行该代码。在很多情况下,此时您只需删除一些导入。
    • 事实上,要解决打印语句的问题,您既不需要six 也不需要future,只需要文件开头的from __future__ import print_function 行。它是标准 python 的一部分。但是six / future 解决了许多其他问题,例如 range / xrange、某些模块的名称更改……
    • rajah9:FWIW,在某些情况下,six 模块使用了我的答案中显示的exec() 技巧——至少这是我想到使用它来避免语法错误的地方。另请注意,在不需要import print_function 的情况下,以在 Python 2 和 3 中都可以使用的方式使用 print 相当简单。
    【解决方案4】:

    我怀疑这是否值得,但作为概念证明:您可以结合使用装饰器和内置 exec() 函数。使用exec() 是一种避免由于语言差异而导致语法错误的方法。

    这就是我的意思:

    import sys
    
    sys_vers_major, sys_vers_minor, sys_vers_micro = sys.version_info[:3]
    sys_vers = sys_vers_major + sys_vers_minor*.1 # + sys_vers_micro*.01
    print('sys_vers: %s' % sys_vers)
    
    def python_version(vers, code1, code2):
        lcls = {}  # Dictionary to temporarily store  version of function defined.
    
        if sys_vers == vers:
            exec(code1, globals(), lcls)
        else:
            exec(code2, globals(), lcls)
    
        def decorator(func):
            return lcls[func.__name__]
    
        return decorator
    
    
    @python_version(2.7,
    """
    def geo_point(lat, lng):
        print('code1 version')
    """,
    """
    def geo_point(lat: float, lng: float):
        print('code2 version')
    """)
    def geo_point(): pass  # Needed to know name of function that's being defined.
    
    geo_point(121, 47)  # Show which version was defined.
    

    【讨论】:

    • 有趣的想法,如果真的需要可能会很有趣
    • @gelonida:是的,我这样做主要是为了看看它是否可行。我忽略的一个问题是比较版本 - 这实际上是一个相当复杂的主题 - 请参阅How do I compare version numbers in Python? 无论如何,如果您发现我的回答有用或有帮助,请考虑支持它。
    • 嗯,为什么在这种特殊情况下不只是 if sys.version_info &lt; (3, 0) ?但你是对的。您尝试使您的版本比较非常通用。我只会选择次要/主要版本,这就是语法通常中断的地方。
    • 我考虑将vers 参数设为一个元组,但它似乎不够可读(并且比较版本并不是真正的主题)。如您所见,我的答案中的代码只是比较主要+次要。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-02-28
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-01-08
    相关资源
    最近更新 更多