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

时间:2021-05-27 23:18:21

Basically, what I'd like to do is:

基本上,我想做的是:

>>> 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

But more generally the question is, how to fill specific positional arguments with partial.

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

P.S. I do realize that I can use a lambda instead.

附:我确实意识到我可以使用lambda代替。

3 个解决方案

#1


36  

It cannot be done. You have to make a wrapper function.

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

Ostensibly, you would use keyword arguments, as you tried to do - that's what they're for, right? Unfortunately, as you've discovered, python's standard library functions do not take named parameters. Thus, it is not possible given the current implementation of partial without making another function to run interference.

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

According to the acceptance of PEP 309, what was accepted for inclusion was "a partial() type constructor binding leftmost positional arguments and any keywords." Furthermore, it states:

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

Note that when the object is called as though it were a function, positional arguments are appended to those provided to the constructor, and keyword arguments override and augment those provided to the constructor.

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

Positional arguments, keyword arguments or both can be supplied at when creating the object and when calling it.

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

Because additional positional arguments are appended, there would be no way to supply some preceding positional argument (by this implementation).

由于附加了额外的位置参数,因此无法提供某些先前的位置参数(通过此实现)。

However, it goes on:

然而,它继续:

Partially applying arguments from the right, or inserting arguments at arbitrary positions creates its own problems, but pending discovery of a good implementation and non-confusing semantics, I don't think it should be ruled out.

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

So, it apparently could be on the table, but as it stands, it is not implemented that way.

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

For the sake of disclosure, emphasis in quotes above was my own.

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

#2


3  

If you really need this you can use rpartial from funcy 3rd-party library.

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

Its code is here:

它的代码在这里:

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

So, your case can be handled as following:

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

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

#3


0  

Use this code:

使用此代码:

# See: functoolspartial for binding...

class Function(object):
    def __init__(self, fn):
        self.fn = fn

    def __call__(self, *args, **kwargs):
        return self.fn(*args, **kwargs)

    def __add__(self, other):
        def subfn(*args, **kwargs):
            return self(*args, **kwargs) + other(*args, **kwargs)
        return subfn


class arg(object):
    """Tagging class"""
    def __init__(self, index):
        self.index = index


class bind(Function):
    """Reorder positional arguments.
    Eg: g = f('yp', _1, 17, _0, dp=23)
    Then g('a', 'b', another=55) --> f('yp', 'b', 17, 'a', dp=23, another=55)
    """

    def __init__(self, fn, *pargs, **pkwargs):
        # Maximum index referred to by the user.
        # Inputs to f above this index will be passed through
        self.fn = fn
        self.pargs = pargs
        self.pkwargs = pkwargs
        self.max_gindex = max(
            (x.index if isinstance(x, arg) else -1 for x in pargs),
            default=-1)

    def __call__(self, *gargs, **gkwargs):
        fargs = \
            [gargs[x.index] if isinstance(x, arg) else x for x in self.pargs] + \
            list(gargs[self.max_gindex+1:])

        fkwargs = dict(self.pkwargs)
        fkwargs.update(gkwargs)    # Overwrite keys
        return self.fn(*fargs, *fkwargs)

Then try it out like:

然后尝试一下:

def myfn(a,b,c):
print(a,b,c)

bind(myfn, arg(1), 17, arg(0))(19,14)

#1


36  

It cannot be done. You have to make a wrapper function.

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

Ostensibly, you would use keyword arguments, as you tried to do - that's what they're for, right? Unfortunately, as you've discovered, python's standard library functions do not take named parameters. Thus, it is not possible given the current implementation of partial without making another function to run interference.

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

According to the acceptance of PEP 309, what was accepted for inclusion was "a partial() type constructor binding leftmost positional arguments and any keywords." Furthermore, it states:

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

Note that when the object is called as though it were a function, positional arguments are appended to those provided to the constructor, and keyword arguments override and augment those provided to the constructor.

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

Positional arguments, keyword arguments or both can be supplied at when creating the object and when calling it.

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

Because additional positional arguments are appended, there would be no way to supply some preceding positional argument (by this implementation).

由于附加了额外的位置参数,因此无法提供某些先前的位置参数(通过此实现)。

However, it goes on:

然而,它继续:

Partially applying arguments from the right, or inserting arguments at arbitrary positions creates its own problems, but pending discovery of a good implementation and non-confusing semantics, I don't think it should be ruled out.

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

So, it apparently could be on the table, but as it stands, it is not implemented that way.

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

For the sake of disclosure, emphasis in quotes above was my own.

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

#2


3  

If you really need this you can use rpartial from funcy 3rd-party library.

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

Its code is here:

它的代码在这里:

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

So, your case can be handled as following:

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

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

#3


0  

Use this code:

使用此代码:

# See: functoolspartial for binding...

class Function(object):
    def __init__(self, fn):
        self.fn = fn

    def __call__(self, *args, **kwargs):
        return self.fn(*args, **kwargs)

    def __add__(self, other):
        def subfn(*args, **kwargs):
            return self(*args, **kwargs) + other(*args, **kwargs)
        return subfn


class arg(object):
    """Tagging class"""
    def __init__(self, index):
        self.index = index


class bind(Function):
    """Reorder positional arguments.
    Eg: g = f('yp', _1, 17, _0, dp=23)
    Then g('a', 'b', another=55) --> f('yp', 'b', 17, 'a', dp=23, another=55)
    """

    def __init__(self, fn, *pargs, **pkwargs):
        # Maximum index referred to by the user.
        # Inputs to f above this index will be passed through
        self.fn = fn
        self.pargs = pargs
        self.pkwargs = pkwargs
        self.max_gindex = max(
            (x.index if isinstance(x, arg) else -1 for x in pargs),
            default=-1)

    def __call__(self, *gargs, **gkwargs):
        fargs = \
            [gargs[x.index] if isinstance(x, arg) else x for x in self.pargs] + \
            list(gargs[self.max_gindex+1:])

        fkwargs = dict(self.pkwargs)
        fkwargs.update(gkwargs)    # Overwrite keys
        return self.fn(*fargs, *fkwargs)

Then try it out like:

然后尝试一下:

def myfn(a,b,c):
print(a,b,c)

bind(myfn, arg(1), 17, arg(0))(19,14)