如何在Python中使用partial填充特定位置参数?

68 投票
5 回答
28975 浏览
提问于 2025-04-17 04:34

基本上,我想做的是:

>>> from functools import partial
>>> partial(str.startswith, prefix='a')('a')
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: startswith() takes no keyword arguments

但更一般来说,我的问题是,如何用 partial 填充特定的位置参数

附注:我知道我也可以使用 lambda

5 个回答

14

如果你真的需要这个功能,可以使用来自 funcy 这个第三方库的 rpartial

它的代码在这里:

def rpartial(func, *args):
    return lambda *a: func(*(a + args))

所以,你的情况可以这样处理:

>>> startswith_a = rpartial(str.startswith, 'a')
>>> startswith_a('abc')
True
>>> startswith_a('def')
False
32

我知道这个问题有点老了,但我经常遇到这个情况,我觉得我找到了一个不错的解决办法。我使用了一个稍微修改过的 partial,它用一个叫做 Ellipses 的对象(...)作为占位符值,如果在创建对象时你还不知道具体的值,这个方法非常有用!

这是 partial 的原始 __call__ 方法:

def __call__(self, /, *args, **keywords):
    keywords = {**self.keywords, **keywords}
    return self.func(*self.args, *args, **keywords)

我们可以用字面意思的 ... 作为一个特殊情况,来表示一个占位符值。

>>> type(...)
<class 'ellipsis'>

下面是整个实现的代码:

class bind(partial):
    """
    An improved version of partial which accepts Ellipsis (...) as a placeholder
    """
    def __call__(self, *args, **keywords):
        keywords = {**self.keywords, **keywords}
        iargs = iter(args)
        args = (next(iargs) if arg is ... else arg for arg in self.args)
        return self.func(*args, *iargs, **keywords)

使用起来相当简单。

def foo(a, b, c, /, *, d):
    print(f"A({a}) B({b}) C({c}) D({d})")

f1 = bind(foo, 1, 2, 3, d=4)
f1()

f2 = bind(foo, 1, 2, d=4)
f2(3)

f3 = bind(foo, 1, ..., 3, d=4)
f3(2)

f4 = bind(foo, ..., 2, ..., d=4)
f4(1, 3)

f5 = bind(foo, ..., d=5)
f5(1, 2, 3, d=4)
73

这件事是做不到的。你需要写一个包装函数。

表面上看,你是想用关键字参数,正如你尝试的那样——这不就是它们的用途吗?可惜的是,正如你发现的,Python的标准库函数并不支持命名参数。因此,按照现在的实现方式,使用partial是无法做到的,必须再写一个函数来处理这个问题。

根据PEP 309的内容,接受的方案是“一个partial()类型的构造函数,可以绑定最左边的位置参数和任何关键字参数。”此外,它还说明:

注意,当这个对象被调用时,位置参数会附加到构造函数提供的参数后面,而关键字参数会覆盖并增加构造函数提供的参数。

在创建对象和调用对象时,可以提供位置参数、关键字参数或两者。

因为额外的位置参数是附加的,所以在这种实现下,无法提供一些前面的参数。

不过,它还提到:

右侧部分应用参数,或者在任意位置插入参数会产生自己的问题,但在找到好的实现和不混淆的语义之前,我认为不应该排除这种可能性。

所以,这种方法似乎是有可能的,但目前并没有这样实现。

为了说明,以上引号中的强调是我自己的。

撰写回答