如何用python中的partial填充特定的位置参数?

new*_*ver 41 python

基本上,我想做的是:

>>> 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
Run Code Online (Sandbox Code Playgroud)

但更普遍的问题是,如何用特定的位置参数填充partial.

PS我确实意识到我可以用一个lambda代替.

Nat*_*ate 51

它无法完成.你必须做一个包装函数.

表面上看,你会尝试使用关键字参数 - 这就是它们的用途,对吧?不幸的是,正如您所发现的那样,python的标准库函数不会使用命名参数.因此,在partial没有使另一个功能运行干扰的情况下给出当前实现是不可能的.

根据对PEP 309的接受,接受包含的是"部分()类型构造函数绑定最左侧的位置参数和任何关键字." 此外,它指出:

请注意,当对象被调用时,就好像它是一个函数一样,位置参数被附加到提供给构造函数的那些参数,并且关键字参数会覆盖并扩充提供给构造函数的参数.

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

因为附加的位置参数被所附,就没有办法提供一些前述位置参数(由此实现).

然而,它继续:

右边部分应用参数,或在任意位置插入参数会产生自己的问题,但是在发现良好的实现和非混淆语义之前,我认为不应该排除它.

所以,它显然可以在桌面上,但就目前而言,它并没有那样实现.

为了便于披露,上面引用的重点是我自己的.


obl*_*lex 6

如果你真的需要这个,你可以使用rpartialfuncy第三方库。

它的代码在这里:

def rpartial(func, *args):
    return lambda *a: func(*(a + args))
Run Code Online (Sandbox Code Playgroud)

因此,您的案件可以按以下方式处理:

>>> startswith_a = rpartial(str.startswith, 'a')
>>> startswith_a('abc')
True
>>> startswith_a('def')
False
Run Code Online (Sandbox Code Playgroud)


Goo*_*ies 6

我知道这很旧,但我一直遇到这个问题,我想我已经创建了一个巧妙的解决方案。如果您在构建对象时不知道它,我使用了一个稍微修改过的版本,partial它使用一个 Ellipses 对象 ( ...) 作为占位符值。这对这个非常有用!

这是原始__call__方法partial

def __call__(self, /, *args, **keywords):
    keywords = {**self.keywords, **keywords}
    return self.func(*self.args, *args, **keywords)
Run Code Online (Sandbox Code Playgroud)

相反,我们可以使用文字...作为特殊情况来指示占位符值

>>> type(...)
<class 'ellipsis'>
Run Code Online (Sandbox Code Playgroud)

这是整个实现:

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)
Run Code Online (Sandbox Code Playgroud)

用法相当简单

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)
Run Code Online (Sandbox Code Playgroud)

  • 您应该向 python-dev 邮件列表提出这个建议!特别是因为 PEP 309 表明对此类提案持一定的开放态度。 (11认同)