这听起来是个坏主意——将 Python 2.5 代码解释为 Python 3 时会遇到更严重的问题,比如每个 except 语句都是语法错误,字符串类型错误(或者,如果你解决这个问题,s[i] 返回一个 int 而不是一个字节),等等。
这里要做的显而易见的事情是将代码移植到仍然受支持的 Python。
如果由于某种原因确实无法做到这一点,最简单的做法可能是围绕您需要运行的代码编写一个简单的 Python 2.5 包装器,该包装器通过sys.argv 和/或sys.stdin 获取输入并返回通过sys.exit 和/或sys.stdout 获得结果。
那么,你可以这样称呼它:
p = subprocess.run(['python2.5', 'mywrapper.py', *args], capture_output=True)
if p.retcode:
raise Exception(p.stderr.decode('ascii'))
results = p.stdout.splitlines().decode('ascii')
但如果你真的想这样做,而这确实是你唯一的问题……这仍然不是方法。
您必须低于 C API 的级别,进入像 struct PyFloat_Type 这样的内部类型对象,访问它们的 tp_as_number 结构,并将它们的 nb_floordiv 函数复制到它们的 nb_truediv 插槽。即使这样也不会改变一切。
更好的解决方案是构建一个导入钩子,在编译 AST 之前对其进行转换。
编写导入钩子可能是一个太大的话题,无法在几段中作为答案的前言进行介绍,因此请参阅 this question 了解该部分。
现在,至于 import 钩子的实际作用,您要做的是替换 MyLoader.exec_module 方法。而不是这个:
def exec_module(self, module):
with open(self.filename) as f:
data = f.read()
# manipulate data some way...
exec(data, vars(module))
你要这样做:
def exec_module(self, module):
with open(self.filename) as f:
data = f.read()
tree = ast.parse(data)
# manipulate tree in some way
code = compile(tree, self.filename, 'exec')
exec(code, vars(module))
那么,我们如何“以某种方式操纵树”?通过构建NodeTransformer。
每个/ 表达式都是一个BinOp 节点,其中op 是没有属性的Div 节点,left 和right 是要划分的值。如果我们想将其更改为相同的表达式,但使用//,那就是相同的BinOp,但op 是FloorDiv。
所以,我们可以只访问Div 节点并将它们变成FloorDiv 节点:
class DivTransformer(ast.NodeTransformer):
def visit_Div(self, node):
return ast.copy_location(ast.FloorDiv(), node)
我们的“#以某种方式操作树”变成:
tree = DivTransformer().visit(tree)
如果您想根据除数是否为整数文字在floordiv 和truediv 之间进行选择,正如您的示例所暗示的那样,这并不难:
class DivTransformer(ast.NodeTransformer):
def visit_BinOp(self, node):
if isinstance(node.op, ast.Div):
if isinstance(node.right, ast.Num) and isinstance(node.right.val, int):
return ast.copy_location(ast.BinOp(
left=node.left,
op=ast.copy_location(ast.FloorDiv(), node.op),
right=node.right))
return node
但我怀疑那是你真正想要的。事实上,你真正想要的可能很难定义。你可能想要这样的东西:
-
floordiv 如果两个参数在运行时都是整数值
-
floordiv 如果最终控制 __*div__/__*rdiv__ 的参数(通过精确复制解释器为此使用的规则)是一个整数值。
- ……还有别的吗?
无论如何,这样做的唯一方法是将BinOp 替换为Call 到您编写的mydiv 函数,例如,坚持builtins。然后该函数执行类型切换以及实现您的规则所需的任何其他内容,然后是 return a/b 或 return a//b。