如何基于任意字符串创建具有任意参数的函数

How to create a function with arbitrary parameters based on an arbitrary string

我的最终目标是:我想创建一组真值tables,其中每个真值table对应一个任意定义的布尔表达式,它最初存储为一个字符串(比如:“(var_1 而不是 var_2)或 var_3”)。该字符串可以有任意数量的运算符。

如果我有一个特定的布尔表达式,这很容易实现:

def evaluator(var_1,var_2,var_3):
    return  (var_1 and not var_2) or var_3

def truth_table(f):
    values = [list(x) + [f(*x)] for x in product([False,True], repeat=f.__code__.co_argcount)]
    return pd.DataFrame(values,columns=(list(f.__code__.co_varnames) + [f.__name__]))

one_truth_table = truth_table(evaluator)

但我想对具有任意数量参数的任意函数以及任意类型的布尔表达式执行此操作。我将遍历布尔表达式作为字符串来创建一系列真值 tables.

我整天都在为此苦苦挣扎。如果我能让这段代码按照我想要的方式运行,那么我的问题就解决了。

def temp_func(boolean_expression_string,variable_names_list):
    return eval(boolean_expression_string)

# i have two strings: '(var_1 and var_2) and (var_3 or not var_4) or var_etc'
# and also: 'var_1,var_2,var_3,var_4,var_etc'

temp_func('(var_1 and var_2) and (var_3 or not var_4) or var_etc', input(list(eval('var_1,var_2,var_3,var_4,var_etc'))))

运行 结果是:

NameError: name 'var_1' is not defined

我包括了整个背景故事,以防我以愚蠢的方式解决整个问题。虽然您可能猜想我只是想让它发挥作用,但目前优雅并不是我的首要任务。

编辑:变量名定义不统一,无法按照某种顺序解析,又是一层难处理

eval 可以将两个名称到值映射的字典分别用作全局和局部名称空间,以 运行 中的表达式。

新答案

请求原谅比获得许可更容易。

想法是通过尝试计算表达式并捕获 NameError 来查找所有变量名。请注意,我们需要为变量列表生成所有变量赋值,因为 python 将 短路 orand 的计算。例如,在 var_1 or var_2 中,如果 var_1 初始化为 True,我们将找不到 var_2

def variable_names(expression):
    # list of found variables
    variables = list()
    while True:
        try:
            # generate all assignments for current variable names
            assignments = [
                {variables[i]: v for i, v in enumerate(vs)}
                for vs in itertools.product(
                    [True, False], repeat=len(variables)
                )
            ]
            # try to evaluate them all
            for assignment in assignments:
                eval(expression, None, assignment)
            # all of them work, can return
            return variables
        except NameError as e:
            # get next variable
            variables.append(
                re.match("name '(.+)' is not defined", str(e)).group(1)
            )

然后我们生成一个作业字典列表——与之前的方法完全一样——并将其提供给 eval,将结果添加到字典中。然后可以从记录创建 DataFrame。

def truth_table(expression):
    # get variable names
    variables = variable_names(expression)
    # make list of assignments
    assignments = [
        {variables[i]: v for i, v in enumerate(vs)}
        for vs in itertools.product([True, False], repeat=len(variables))
    ]
    # get truthy values
    values = [
        {**assignment, **{"value": eval(expression, None, assignment)}}
        for assignment in assignments
    ]
    # make dataframe from records and supply column order
    return pd.DataFrame.from_records(values, columns=variables + ["value"])

旧答案——固定变量命名。

如果你所有的变量都被命名为 var_1, var_2,...你可以将它们的值赋值列表提供给 evaluator 并将它们解析成字典

def evaluator(expression, values):
    return eval(
        expression,
        None,
        {"var_{}".format(i + 1): v for i, v in enumerate(values)},
    )

和运行如下

evaluator( 
    "(var_1 and var_2) and (var_3 or not var_4)", 
    [True, False, True, False] 
)                                                                                        

其中 returns False.

从表达式计算真值 table 的完整代码是

def truth_table(expression):
    # get variable names
    varnames = set(re.findall(r"(var_\d+)", expression))
    # sort by index
    varnames = sorted(varnames, key=lambda x: int(x.split("_")[1]))
    # get truthy values
    values = [
        list(x) + [evaluator(expression, x)]
        for x in itertools.product([True, False], repeat=len(varnames))
    ]
    return pd.DataFrame(values, columns=varnames + ["T/F"])

如果变量列表中有空洞——例如(var_1 and var_3)—您需要在调用评估器之前重命名它们或更改评估器以获取字典。