运行时使用python中的参数编译函数

问题描述 投票:0回答:1

我正在尝试使用compile来运行时生成接受参数的Python函数,如下所示。

import types
import ast

code = compile("def add(a, b): return a + b", '<string>', 'exec')
fn = types.FunctionType(code, {}, name="add")
print(fn(4, 2))

但它失败了

TypeError: <module>() takes 0 positional arguments but 2 were given

反正有没有使用这种方式编译接受参数的函数,还是有其他方法可以做到这一点?

python-3.x codegen
1个回答
1
投票

Compile返回代码对象以创建模块。在Python 3.6中,如果要反汇编代码对象:

>>> import dis
>>> dis.dis(fn)
 0 LOAD_CONST    0 (<code object add at ...., file "<string>" ...>)
 2 LOAD_CONST    1 ('add')
 4 MAKE_FUNCTION 0
 6 STORE_NAME    0 (add)
 8 LOAD_CONST    2 (None)
10 RETURN_VALUE

这直译为make function; name it 'add'; return None

此代码表示您的函数运行模块的创建,而不是返回模块或函数本身。基本上,你实际做的是等同于以下内容:

def f():
    def add(a, b):
        return a + b

print(f(4, 2))

对于你如何解决的问题,答案是它取决于你想做什么。例如,如果你想使用compile编译一个函数,简单的答案就是你不能没有做类似下面的事情。

# 'code' is the result of the call to compile.
# In this case we know it is the first constant (from dis),
# so we will go and extract it's value
f_code = code.co_consts[0]
add = FunctionType(code, {}, "add")

>>> add(4, 2)
6

由于在Python中定义函数需要运行Python代码(除了编译为字节码之外,默认情况下没有静态编译),您可以传入自定义globalslocals字典,然后从这些字典中提取值。

glob, loc = {}, {}
exec(code, glob, loc)

>>> loc['add'](4, 2)
6

但真正的答案是,如果你想这样做,最简单的方法通常是使用Abstract Syntax Trees生成ast module,并将其编译成模块代码并评估或执行模块。

如果你想进行字节码转换,我建议你看一下PyPi上的codetransformer包。

TL;使用compile的DR将只返回模块的代码,并且最严重的代码生成可以通过AST或通过操作字节代码来完成。

© www.soinside.com 2019 - 2024. All rights reserved.