如何从Python 2.7扩展/重载range()函数来接受浮动?

时间:2016-09-02 06:21:34

标签: python python-2.7 python-2.6

我们的核心应用程序从Python 2.6变为Python 2.7,可能在以后的版本中变为Python 3。

更改了python的范围函数(引自Python 2.7更改日志)。

  

range()函数更一致地处理其参数;它   现在将在非浮动上调用__int__()

我们允许用户根据我们进一步处理的结果添加表达式/ Python代码。

现在如何更改range功能?其中一些正在使用float参数,这些参数现在在Python 2.7中失败了。

由于代码是由用户编写的,我们无法更改Python代码/表达式。有1000个文件。有些用户可能拥有自己的文件。

  1. 是否有办法从Python扩展range()函数,以便它将采用浮动参数?

  2. 另一种方法是解析python代码并将float更改为int。这是非常耗时的,因为它需要大量的刺痛操作,并且一些range调用将公式作为参数。

  3. 我们的应用程序使用C ++构建,我们使用C ++ python APIS评估python表达式

2 个答案:

答案 0 :(得分:3)

您遇到严重的迁移问题。如果你想切换到Python 2.7甚至是Python 3,那么最终重构现有的(用户)代码库基本上没有简单的方法。恕我直言,您有以下选择。

  1. 为您的用户提供永久(非可选)2.6兼容接口。这意味着他们不必改变任何东西,但它有点破坏升级到Python 2.7的目的,因为用户仍然必须满足Python 2.6语义。 判决:不推荐。

  2. 在有限的时间内提供临时(非可选)2.6兼容接口。在这种情况下,现有代码最终需要重构。 判决:不推荐。

  3. 让用户在其代码中包含一个标志(例如,可以安全地识别的魔术注释,而不执行像# *$$ supertool-pythonversion: 2.7 $$*这样的文件),代码期望运行的Python版本并使用2.6兼容性仅适用于未使用Python 2.7标记的文件。这样,您可以执行任何兼容性攻击,以运行旧文件并按原样运行新文件。 结论:增加了复杂性,但可以帮助您进行迁移。 <强>推荐

  4. 但是,您可以方便地从C ++调用Python。因此,您可以通过传递给globals的{​​{1}}和locals字典来控制脚本运行的环境。为了实现方案3,在检查文件中的兼容性标志之后,您可以在调用PyEval_EvalCode“启用”之前将支持range参数的自定义float函数放入gloabls字典中兼容模式。

    我不熟悉Python的C API,但在Python中看起来像这样(并且可以通过C API执行相同的操作):

    PyEval_EvalCode

答案 1 :(得分:2)

更改不是在非浮动上调用__int__。影响你的变化是Python 2.7中不再接受float参数:

Python 2.6.9 (default, Sep 15 2015, 14:14:54) 
[GCC 5.2.1 20150911] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> range(10.0)
__main__:1: DeprecationWarning: integer argument expected, got float
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

请参阅DeprecationWarning - 您的代码一直发出警告,但您选择忽略它们。在Python 2.7中:

Python 2.7.12 (default, Jul  1 2016, 15:12:24) 
[GCC 5.4.0 20160609] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> range(10.0)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: range() integer end argument expected, got float.

解决方案是将range()包装到一个新函数中:

def py26_range(*args):
    args = [int(i) if isinstance(i, float) else i for i in args]
    return range(*args)

此函数强制浮点数保留Python 2.6行为的整数。然后,您需要在参数可能是浮点数的代码部分中将range的所有用法替换为py26_range

如果您非常绝望,可以将此版本的range安装到__builtin__中:

from functools import wraps
import __builtin__

@wraps(range)
def py26_range(*args):
    args = [int(i) if isinstance(i, float) else i for i in args]
    return range(*args)    

__builtin__.range = py26_range

在其他模块甚至导入之前执行此操作,它应该像以前一样工作。