我正在测试一些代码来回答Possible to turn an input string into a callable function object in Python?,并且该问题被重复了,但我在源答案中找不到该问题的答案。
SO上有许多与此类似的答案,但是都没有解决如何从function
返回string
的问题。 exec()
不返回function.
,它仅执行任意代码。
我的问题是,将字符串转换为函数并作为函数对象返回的正确方法是吗?
my_string = "def add5(x):return x + 5"
def string_to_func(my_string):
exec(my_string)
for var_name, var in locals().items():
if callable(var):
return var
print("no callable in string")
#fallback code.
add5 = string_to_func(my_string)
print(add5(3))
PS :如果原始问题重新打开,我将删除此问题。
答案 0 :(得分:1)
粗略地说,如果您假设输入是完全受信任的,并且所提供的字符串具有有效的Python语法,则会产生一个可调用的函数,并且如果exec
不能使用,可以提供类似的东西
import ast
import types
def string_to_function(source):
tree = ast.parse(source)
if len(tree.body) != 1 or not isinstance(tree.body[0], ast.FunctionDef):
raise ValueError('provided code fragment is not a single function')
co = compile(tree, 'custom.py', 'exec')
# first constant should be the code object for the function
return types.FunctionType(co.co_consts[0], {})
示例:
f = string_to_function("""
def my_function(x):
return x + 5
"""
)
print('f = %d' % f(5))
输出
f = 10
该代码确保所提供的源具有单个函数定义,并假定生成的字节码的组织方式(即,仅适用于内置于当前版本的Python中的编译器,其中,生成的字节码用于放置代码对象为co_consts
中第0个元素中的源中定义的单个函数)。此答案的previous version使用了exec
(提问者“无论如何都不是exec的忠实拥护者”),其执行方式是将结果绑定到特定目标中,并且该方法应该更多可靠,因为它不涉及较低级别的结构,尽管可以使用此处使用的ast
模块进行完整性检查,而不是该原始版本。
还请注意,此答案仅适用于Python 3 +。
进一步编辑:
实际上,我对使用exec
来执行(固定输入)任意代码的用法感到有些m贬,仅仅是因为它实际上实际上经常被很多人误解了。 。在这种特殊情况下,如果验证仅接受特定的来源,则并不一定意味着每个语句都将立即执行。在这种情况下尤其如此(在我最初的懒惰答案中不能正确保证 ,但这是真正了解框架实际作用的地方,这对于进行涉及动态编译的任何事情都很重要。语言框架中的代码,并考虑到需要更高级别的“安全性”(事实相反之后立即执行功能,因此我最初并未实现),因此在编辑中使用了ast
,现在客观上更好)。
因此,在给定单个函数定义的源输入的情况下,调用exec(co)
到底具有什么作用呢?可以通过查看字节码来确定,如下所示:
>>> dis.dis(co)
2 0 LOAD_CONST 0 (<code object my_function at 0x7fdbec44c420, file "custom.py", line 2>)
2 LOAD_CONST 1 ('my_function')
4 MAKE_FUNCTION 0
6 STORE_NAME 0 (my_function)
8 LOAD_CONST 2 (None)
10 RETURN_VALUE
它要做的就是加载代码对象,使其成为一个适当的函数,并将结果分配给my_function
(在当前相关的范围内),实际上就是这样。因此,是的,正确的方法是验证源绝对是此处的函数定义(因为通过检查AST进行的验证比仅包含一条语句的更幼稚的验证更为安全),然后在特定的{ {1}}并从那里提取任务。考虑到提供的任何功能都将立即执行,因此在这种情况下使用dict
本质上不会(或更多)安全。