我们的核心应用程序从Python 2.6变为Python 2.7,可能在以后的版本中变为Python 3。
更改了python的范围函数(引自Python 2.7更改日志)。
range()
函数更一致地处理其参数;它 现在将在非浮动上调用__int__()
。
我们允许用户根据我们进一步处理的结果添加表达式/ Python代码。
现在如何更改range
功能?其中一些正在使用float
参数,这些参数现在在Python 2.7中失败了。
由于代码是由用户编写的,我们无法更改Python代码/表达式。有1000个文件。有些用户可能拥有自己的文件。
是否有办法从Python扩展range()
函数,以便它将采用浮动参数?
另一种方法是解析python代码并将float
更改为int
。这是非常耗时的,因为它需要大量的刺痛操作,并且一些range
调用将公式作为参数。
我们的应用程序使用C ++构建,我们使用C ++ python APIS评估python表达式
答案 0 :(得分:3)
您遇到严重的迁移问题。如果你想切换到Python 2.7甚至是Python 3,那么最终重构现有的(用户)代码库基本上没有简单的方法。恕我直言,您有以下选择。
为您的用户提供永久(非可选)2.6兼容接口。这意味着他们不必改变任何东西,但它有点破坏升级到Python 2.7的目的,因为用户仍然必须满足Python 2.6语义。 判决:不推荐。
在有限的时间内提供临时(非可选)2.6兼容接口。在这种情况下,现有代码最终需要重构。 判决:不推荐。
让用户在其代码中包含一个标志(例如,可以安全地识别的魔术注释,而不执行像# *$$ supertool-pythonversion: 2.7 $$*
这样的文件),代码期望运行的Python版本并使用2.6兼容性仅适用于未使用Python 2.7标记的文件。这样,您可以执行任何兼容性攻击,以运行旧文件并按原样运行新文件。 结论:增加了复杂性,但可以帮助您进行迁移。 <强>推荐强>
但是,您可以方便地从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
在其他模块甚至导入之前执行此操作,它应该像以前一样工作。