将输入字符串转换为可调用函数并将其存储为变量的正确方法?

2024-04-19 06:48:17 发布

您现在位置:Python中文网/ 问答频道 /正文

我正在测试一些代码来回答Possible to turn an input string into a callable function object in Python?,问题以重复的形式结束了,但我在源代码答案中找不到该问题的答案。你知道吗

关于这个问题有很多类似的答案,但是没有一个解决如何从string返回function的问题。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))

p.S.:如果重新打开原始问题,我将删除此问题。你知道吗


Tags: to函数答案代码instringreturnvar
1条回答
网友
1楼 · 发布于 2024-04-19 06:48:17

粗略地说,如果假设输入是完全受信任的,并且提供的字符串是有效的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模块的健全性检查可以包含在原始版本中。你知道吗

还要注意,这个答案只适用于python3+。你知道吗

进一步编辑:

关于exec的用法被假定为执行任意代码(在固定输入上)的评论,实际上我还是有点恼火,因为它的实际功能常常被误解。在这种特殊情况下,如果验证只接受特定的源,则不一定意味着每个语句都立即执行。尤其是在这种情况下(在我最初的懒散回答中,并不能保证这一点,但这正是真正理解框架实际上在做什么对于在语言框架中进行代码动态编译非常重要的地方,考虑到需要更高级别的“安全性”(在事实否定它之后立即执行函数,因此我最初没有实现),使用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进行的验证比只存在一条语句的更简单的验证更安全),然后在特定的dict上运行exec并从中提取赋值。在这种情况下,以这种方式使用exec并不是天生的不安全(或更安全),因为所提供的任何函数都会立即执行。你知道吗

相关问题 更多 >