1

我有一个无法控制的 v2.5 Python 代码,因为它是从支持 Python v2.5 的第三方软件导出的。

我的机器上有 Python v3.3,我想以某种方式使用 C API 模拟 v2.5。我主要关心的是 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

谢谢

4

1 回答 1

4

这听起来是个坏主意——如果将 Python 2.5 代码解释为 Python 3,您将遇到更严重的问题,例如每个except语句都是语法错误,字符串类型错误(或者,如果您解决了这个问题,则s[i]返回一个int 而不是一个字节),等等。


这里要做的显而易见的事情是将代码移植到仍然受支持的 Python 上。

如果由于某种原因这确实是不可能的,那么最简单的做法可能是围绕您需要运行的代码编写一个简单的 Python 2.5 包装器,该包装器通过 and/or 获取输入并通过and sys.argv/orsys.stdin返回结果。sys.exitsys.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_floordivnb_truediv


一个更好的解决方案是构建一个导入钩子,在编译 AST 之前对其进行转换。

编写导入钩子可能是一个太大的话题,无法在几段中作为答案的前言来涵盖,所以请参阅这个问题的那部分。

现在,至于 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节点,其中opisDiv没有属性的节点,leftandright是要划分的值。如果我们想将其更改为相同的表达式,但使用//,那是相同的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)

如果您想根据除数是否为整数文字进行选择floordivtruediv正如您的示例似乎暗示的那样,这并不难:

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用 a替换你编写Callmydiv函数,例如,插入builtins. 然后该函数执行类型切换以及实现您的规则所需的任何其他内容,然后是return a/bor return a//b

于 2018-07-13T20:23:36.490 回答