从给定组件以编程方式构造 python 函数

Construct a python function programmatically from given components

提问人:sds 提问时间:11/8/2023 最后编辑:sds 更新时间:11/9/2023 访问量:131

问:

我有一个 python 函数列表,例如,

def f1(x, a): return x + a
def f2(x, y, a, b): return x * a + y * b
def f3(c, d, e): return c * d - e
def f4(y, z): return 2 * y - z
...
my_functions = [f1, f2, f3, f4 ...]

我想以预定义的方式组合它们的所有对,例如,通过取一个产品:

my_products = [product(f,g) for f,g in itertools.combinations(my_functions, 2)]

因此,需要 2 个函数,和 ,并生成一个计算其乘积的函数:should returnproductfgproduct(f1,f2)

def f1_f2(x, a, y, b):
    return f1(x=x, a=a) * f2(x=x, y=y, a=a, b=b)

对于Lisp,我会使用一个简单的

使用 Perl,我将创建并使用 eval 的字符串表示。f1_f2

我该如何使用 Python? 编写文件并加载它? 使用 execEVAL编译

PS1的。生成的函数将传递给 curve_fit,因此所有有问题的函数(尤其是 !) 的返回值都必须采用位置参数而不是 kw dicts。product

参数函数将具有必须正确处理的相交的参数名称集。

PS2的如果您认为这是一个 XY 问题,请建议其他方法来生成curve_fit的函数参数。

python 函数 eval

评论

0赞 matszwecja 11/8/2023
几乎从来没有一个很好的理由使用.eval
0赞 Charles Duffy 11/9/2023
如果变量名称不是特殊的,这将非常容易。这在 Python 中是可行的(没有任何可怕的东西,比如 or ),但它需要一些胶水 - 你需要编写内省代码或使用一个为你做这件事的库。evalexec
1赞 Charles Duffy 11/9/2023
(请注意,我在上面对“this”是什么做了一些假设——我假设你正在构造一个函数,该函数接受与两个子函数的并集命名相同的参数,并将这些参数传递到名称匹配的地方,但你并没有明确地拼写出来)。
0赞 Charles Duffy 11/9/2023
...如果您不想接受 kwargs,而是想生成一些类型检查器可以理解其签名的东西,那也需要更多的工作;如果这是你要问的,你应该明确。
2赞 Charles Duffy 11/9/2023
(作为一般的“Python等价物是什么?”的答案,该模块是你在LISP中使用宏的大锤通用工具;但它是笨拙的,如果可以提出要求来允许更简单的东西,往往会有点矫枉过正;不幸的是,这里的要求是......很难理解:你展示了你想要的调用约定,但没有解释它背后的预期逻辑,所以我们被要求弄清楚两个独立的东西:元编程——我们中的一些人已经掌握了——以及你脑海中大概有的逻辑)。ast

答:

0赞 matszwecja 11/9/2023 #1

我建议重写您的函数以接受任意数量的关键字参数。然后,您可以简单地将任何任意关键字参数列表传递给这两个函数,而无需从外部知道它们的签名:

def f1(**kwargs): return kwargs['x'] + kwargs['a']
def f2(**kwargs): return kwargs['x'] * kwargs['a'] + kwargs['y'] * kwargs['b']
def f3(**kwargs): return kwargs['c'] * kwargs['d'] - kwargs['e']
def f4(**kwargs): return 2 * kwargs['y'] - kwargs['z']

def product(f, g):
    def fun(**kwargs):
        return f(**kwargs) * g(**kwargs)
    return fun

foo = product(f1, f2)

print(foo(x = 1, y = 2, a = 3, b = 4, c = 5))

评论

1赞 MisterMiyagi 11/9/2023
如果函数需要某些参数,则这些参数不应是 kwargs 的一部分。
1赞 JonSG 11/9/2023
这也是我解决问题的方式,除非我会这样做.def f1(x, a, **kwargs): return x + a
1赞 Yevhen Kuzmovych 11/9/2023
@JonSG我什至会说更明确地说明kwargs的“一次性”性质。def f1(x, a, **_): return x + a
0赞 JonSG 11/9/2023
@YevhenKuzmovych 当然,这对我来说是完全可以接受的。我只是使用了惯例。**kwargs
0赞 Yevhen Kuzmovych 11/9/2023 #2

为此,您需要过滤每个函数的关键字参数:

def product(f, g):
    def f_g(**kwargs):
        f_kwargs = {k: v for k, v in kwargs.items() if k in f.__code__.co_varnames}
        g_kwargs = {k: v for k, v in kwargs.items() if k in g.__code__.co_varnames}
        return f(**f_kwargs) * g(**g_kwargs)
    return f_g

my_products = [product(f,g) for f,g in itertools.combinations(my_functions, 2)]

评论

0赞 sds 11/9/2023
f_g必须取位置参数
2赞 Yevhen Kuzmovych 11/9/2023
@sds 你怎么知道哪些位置参数去哪里?
1赞 Yevhen Kuzmovych 11/9/2023
...即你如何决定你的例子中的顺序,而不是(说)。x, a, y, bx, y, a, b
2赞 matszwecja 11/9/2023
我觉得我们这里有一个 XY 问题。
2赞 Charles Duffy 11/9/2023
@sds,......在我读到你上面的评论之前,我当然不知道“决定而不是”是这个问题的重点。如果你写问题时只关注 Python 元编程挑战(这是一个有价值的挑战!),而不假设读者会理解 的约束并从中推断出你的需求细节,那将是有帮助的。x,a,y,bx,y,a,bcurve_fit
2赞 MegaIng 11/9/2023 #3

假设您的所有函数都合理简单,即没有默认值且没有仅位置参数,这应该有效:

from inspect import signature, Signature

def product(f, g):
    f_sig = signature(f)
    g_sig = signature(g)
    combined = f_sig.parameters | g_sig.parameters
    f_g_sig = Signature(combined.values())

    def f_g(*args, **kwargs):
        bound = f_g_sig.bind(*args, **kwargs)
        f_res = f(**{name: bound.arguments[name] for name in f_sig.parameters.keys()})
        g_res = g(**{name: bound.arguments[name] for name in g_sig.parameters.keys()})
        return f_res * g_res

    f_g.__signature__ = f_g_sig
    return f_g

我们使用来自两个函数的组合参数列表构造一个对象,然后使用它来绑定传入的参数。甚至应该适用于混合位置和关键字参数。为了正确使用 ,我们还设置了 (几乎没有记录的) 属性。Signaturecurve_fit__signature__

评论

0赞 sds 11/9/2023
很好,谢谢!唉,返回的函数不能腌制。有办法吗?!
0赞 MegaIng 11/9/2023
@sds 将其绑定到全局名称,并确保该函数的 and 正确。否则,您将需要提供一个自定义对象来动态构造这些函数。__name____qualname__