使用 Python v3.x 和 C API 解释 Python v2.5 代码(关于整数除法)
Interpret Python v2.5 code using Python v3.x and C API (with respect to integer division)
我有一个我无法控制的 v2.5 Python 代码,因为它是从支持 Python v2.5 的第三方软件导出的。
我的机器上有 Python v3.3,我想以某种方式模拟 v2.5
使用 C API。我主要关心的是 v2.x 和 v3.x 之间的整数除法。
例如我有下面的代码:
a=1
b=a/2
c=a/2.
我希望以某种方式将其解释为(使用 v3.x):
a=1
b=a//2
c=a/2.
我能做些什么吗?有什么方法可以像我有 Python v2.5 那样解释代码吗?我想 2to3 脚本不适合我的情况,六个模块也不适合。
我也发现了这个问题与我的相关:
Python 2 and Python 3 dual development
谢谢
这听起来像是个坏主意——而且将 Python 2.5 代码解释为 Python 3 会遇到更严重的问题,就像每个 except
语句都是语法一样错误,字符串是错误的类型(或者,如果你修复了它,s[i]
返回一个 int 而不是一个字节),等等。
这里要做的显而易见的事情是将代码移植到仍然受支持的 Python。
如果由于某种原因这真的不可能,最简单的做法可能是围绕您需要 运行 的代码编写一个简单的 Python 2.5 包装器,它通过 sys.argv
and/or sys.stdin
和 returns 通过 sys.exit
and/or 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。
编写导入挂钩可能是一个太大的主题,无法用几段作为答案的前言来涵盖,因此请参阅 了解该部分。
现在,至于导入挂钩的实际作用,您要做的是替换 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))
那么,我们如何"manipulate tree in some way"?通过构建 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)
而我们的“# manipulate tree in some way”变为:
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
.
我有一个我无法控制的 v2.5 Python 代码,因为它是从支持 Python v2.5 的第三方软件导出的。
我的机器上有 Python v3.3,我想以某种方式模拟 v2.5 使用 C API。我主要关心的是 v2.x 和 v3.x 之间的整数除法。
例如我有下面的代码:
a=1
b=a/2
c=a/2.
我希望以某种方式将其解释为(使用 v3.x):
a=1
b=a//2
c=a/2.
我能做些什么吗?有什么方法可以像我有 Python v2.5 那样解释代码吗?我想 2to3 脚本不适合我的情况,六个模块也不适合。
我也发现了这个问题与我的相关: Python 2 and Python 3 dual development
谢谢
这听起来像是个坏主意——而且将 Python 2.5 代码解释为 Python 3 会遇到更严重的问题,就像每个 except
语句都是语法一样错误,字符串是错误的类型(或者,如果你修复了它,s[i]
返回一个 int 而不是一个字节),等等。
这里要做的显而易见的事情是将代码移植到仍然受支持的 Python。
如果由于某种原因这真的不可能,最简单的做法可能是围绕您需要 运行 的代码编写一个简单的 Python 2.5 包装器,它通过 sys.argv
and/or sys.stdin
和 returns 通过 sys.exit
and/or 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。
编写导入挂钩可能是一个太大的主题,无法用几段作为答案的前言来涵盖,因此请参阅
现在,至于导入挂钩的实际作用,您要做的是替换 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))
那么,我们如何"manipulate tree in some way"?通过构建 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)
而我们的“# manipulate tree in some way”变为:
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
.