Python 3.x 在python中编译带有参数的函数的运行时

Python 3.x 在python中编译带有参数的函数的运行时,python-3.x,codegen,Python 3.x,Codegen,我试图使用运行时生成一个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)) 导入类型 导入ast code=compile(“def add(a,b):返回a+b”,“exec”) fn=types.

我试图使用运行时生成一个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))
导入类型
导入ast
code=compile(“def add(a,b):返回a+b”,“exec”)
fn=types.FunctionType(代码,{},name=“add”)
印刷品(fn(4,2))
但它失败了

TypeError:()接受0个位置参数,但提供了2个


用这种方式编译接受参数的函数还是用其他方式编译?

编译返回代码对象以创建模块。在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
对于你如何应对的问题,答案是这取决于你想做什么。例如,如果您想使用
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(f_code, {}, "add")

>>> add(4, 2)
6
由于在Python中定义函数需要运行Python代码(默认情况下,除了编译为字节码之外,没有静态编译),因此可以传入自定义的
全局
局部
字典,然后从中提取值

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

>>> loc['add'](4, 2)
6
但真正的答案是,如果您想这样做,最简单的方法通常是使用生成,并将其编译为模块代码,然后评估或执行模块

如果您想进行字节码转换,我建议您查看PyPi上的包


TL;DR使用
compile
只会返回模块的代码,最严重的代码生成是使用AST或通过操纵字节码来完成的。

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
对于你如何应对的问题,答案是这取决于你想做什么。例如,如果您想使用
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(f_code, {}, "add")

>>> add(4, 2)
6
由于在Python中定义函数需要运行Python代码(默认情况下,除了编译为字节码之外,没有静态编译),因此可以传入自定义的
全局
局部
字典,然后从中提取值

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

>>> loc['add'](4, 2)
6
但真正的答案是,如果您想这样做,最简单的方法通常是使用生成,并将其编译为模块代码,然后评估或执行模块

如果您想进行字节码转换,我建议您查看PyPi上的包

TL;DR使用
compile
只会返回模块的代码,最严重的代码生成是使用AST或通过操纵字节码来完成的

还有别的办法吗

值得一提的是:我最近创建了一个
@compile\u fun
goodie,它大大简化了在函数上应用
compile
的过程。它依赖于
编译
,因此与上述答案所解释的没有什么不同,但它提供了一种更简单的方法。你的例子写道:

@compile_fun
def add(a, b):
    return a + b

assert add(1, 2) == 3
您可以看到,现在无法使用IDE调试到
add
。请注意,这不会提高运行时性能,也不会保护您的代码不受反向工程的影响,但如果您不希望用户在调试时看到函数的内部结构,这可能会很方便。请注意,明显的缺点是它们无法帮助您调试lib,因此请小心使用

有关详细信息,请参见
makefun

还有别的办法吗

值得一提的是:我最近创建了一个
@compile\u fun
goodie,它大大简化了在函数上应用
compile
的过程。它依赖于
编译
,因此与上述答案所解释的没有什么不同,但它提供了一种更简单的方法。你的例子写道:

@compile_fun
def add(a, b):
    return a + b

assert add(1, 2) == 3
您可以看到,现在无法使用IDE调试到
add
。请注意,这不会提高运行时性能,也不会保护您的代码不受反向工程的影响,但如果您不希望用户在调试时看到函数的内部结构,这可能会很方便。请注意,明显的缺点是它们无法帮助您调试lib,因此请小心使用


有关详细信息,请参见
makefun

您不能先执行
exec(code)
,然后添加(4,2)?谢谢。这很有效。但是我很好奇为什么上面会失败,或者是否有办法修复它。
fn
不是
add()
函数。它是一个函数,在执行时,将在其命名空间中定义一个
add()
函数(您没有保存对该函数的引用,因此无法实际调用
add()
)。@ForceBru您可以这样做,但这通常不是生成代码的方式。@jasonharper这是指
globals()['add']=fn
后接
添加(2,4)
是否有效?(不是这样,所以我认为我理解错了)。你能不能先执行
exec(code)
,然后添加(4,2)?谢谢。这很有效。但是我很好奇为什么上面会失败,或者是否有办法修复它。
fn
不是
add()
函数。它是一个函数,在执行时,将在其命名空间中定义一个
add()
函数(您没有保存对该函数的引用,因此无法实际调用
add()
)。@ForceBru您可以这样做,但这通常不是生成代码的方式。@jasonharper这是指
globals()['add']=fn
后接
添加(2,4)
是否有效?(事实并非如此,所以我认为我理解错了)。有趣。在这种情况下,如何获取对代码对象的引用并使其可调用?(自我记录以备将来使用dis。)@budchanchao我已经更新了我的答案,以涵盖更多细节阅读了我想替换co的行add=FunctionType(代码,{},“add”)