Python 3.x 在python中编译带有参数的函数的运行时
我试图使用运行时生成一个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.
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”)