如何为 functool partial() 指定参数位置

How to specify arg position for functool partial()

根据手册,functools partial() 是 'used for partial function application which “freezes” some portion of a function’s arguments and/or keywords resulting in a new object with a simplified signature.'

指定要计算的参数位置的最佳方法是什么?

编辑

请注意,根据评论,要部分评估的函数可能包含命名和未命名的参数(这些函数应该是完全任意的,并且可能是预先存在的)

编辑结束

例如,考虑:

def f(x,y,z):
    return x + 2*y + 3*z 

然后,使用

from functools import partial

两者

partial(f,4)(5,6)

partial(f,4,5)(6)

给32。

但是如果要计算第三个参数 z 或第一个和第三个参数 x 以及 z 怎么办?

有没有方便的方法将位置信息传递给partial,使用decoratordict,其键是所需的arg位置,各自的值是arg价值观?例如传递 xz 位置是这样的:

partial_dict(f,{0:4,2:6})(5)

只需使用关键字参数。使用上面 f 的定义,

>>> g = partial(f, z=10)
>>> g(2, 4)
40
>>> h = partial(f, y=4, z=10)
>>> h(2)
40

请注意,一旦您对给定参数使用关键字参数,您必须对所有剩余参数使用关键字参数。例如,以下内容无效:

>>> j = partial(f, x=2, z=10)
>>> j(4)
TypeError: f() got multiple values for argument 'x'

但继续使用关键字参数是:

>>> j = partial(f, x=2, z=10)
>>> j(y=4)
40

当您使用 functools.partial 时,您存储 *args**kwargs 的值以供以后插值。当您稍后调用 "partially applied" 函数时,functools.partial 的实现有效地将先前提供的 *args**kwargs 分别添加到参数列表的前端和末尾,就像您自己插入了这些参数解包。即,调用

h = partial(1, z=10)
f(4)

大致相当于写作

args = [1]
kwargs = {'z': 10}
f(*args, 4, **kwargs)

因此,您如何向 functools.partial 提供参数的语义与您需要将参数存储在上面的 argskwargs 变量中的方式相同,这样最后调用 f 是明智的。有关更多信息,请查看 functools module documentation

中给出的 functools.partial 的伪实现

不,partial 不是为了在非连续位置冻结位置参数而设计的。

要实现问题中概述的所需行为,您必须想出一个自己的包装函数,如下所示:

def partial_positionals(func, positionals, **keywords):
    def wrapper(*args, **kwargs):
        arg = iter(args)
        return func(*(positionals[i] if i in positionals else next(arg)
            for i in range(len(args) + len(positionals))), **{**keywords, **kwargs})
    return wrapper

这样:

def f(x, y, z):
    return x + 2 * y + 3 * z

print(partial_positionals(f, {0: 4, 2: 6})(5))

输出:

32

为了更容易使用,您可以创建一个新对象专门指定一个位置参数,当顺序列出要冻结的位置参数值时要跳过 partial:

SKIP = object()

def partial_positionals(func, *positionals, **keywords):
    def wrapper(*args, **kwargs):
        arg = iter(args)
        return func(*(*(next(arg) if i is SKIP else i for i in positionals), *arg),
            **{**keywords, **kwargs})
    return wrapper

这样:

def f(x, y, z):
    return x + 2 * y + 3 * z

print(partial_positionals(f, 4, SKIP, 6)(5))

输出:

32