可以部分应用不带关键字参数的函数的第二个参数吗?

时间:2022-12-07 23:21:19

Take for example the python built in pow() function.

以内置pow()函数的python为例。

xs = [1,2,3,4,5,6,7,8]

from functools import partial

list(map(partial(pow,2),xs))

>>> [2, 4, 8, 16, 32, 128, 256]

but how would I raise the xs to the power of 2?

但是我如何将xs提高到2的幂?

to get [1, 4, 9, 16, 25, 49, 64]

获得[1,4,9,16,25,49,64]

list(map(partial(pow,y=2),xs))

TypeError: pow() takes no keyword arguments

I know list comprehensions would be easier.

我知道列表理解会更容易。

10 个解决方案

#1


28  

No

According to the documentation, partial cannot do this (emphasis my own):

根据文档,部分不能这样做(强调我自己):

partial.args

The leftmost positional arguments that will be prepended to the positional arguments

最左侧的位置参数将添加到位置参数之前


You could always just "fix" pow to have keyword args:

你可以随时“修复”pow以获得关键字args:

_pow = pow
pow = lambda x, y: _pow(x, y)

#2


11  

I think I'd just use this simple one-liner:

我想我只是使用这个简单的单线程:

import itertools
print list(itertools.imap(pow, [1, 2, 3], itertools.repeat(2)))

Update:

I also came up with a funnier than useful solution. It's a beautiful syntactic sugar, profiting from the fact that the ... literal means Ellipsis in Python3. It's a modified version of partial, allowing to omit some positional arguments between the leftmost and rightmost ones. The only drawback is that you can't pass anymore Ellipsis as argument.

我还提出了一个比有用的解决方案更有趣的方法。它是一个美丽的语法糖,从......文字意味着Python3中的省略号这一事实中获益。它是partial的修改版本,允许省略最左边和最右边的一些位置参数。唯一的缺点是你不能再将Ellipsis作为参数传递。

import itertools
def partial(func, *args, **keywords):
    def newfunc(*fargs, **fkeywords):
        newkeywords = keywords.copy()
        newkeywords.update(fkeywords)
        return func(*(newfunc.leftmost_args + fargs + newfunc.rightmost_args), **newkeywords)
    newfunc.func = func
    args = iter(args)
    newfunc.leftmost_args = tuple(itertools.takewhile(lambda v: v != Ellipsis, args))
    newfunc.rightmost_args = tuple(args)
    newfunc.keywords = keywords
    return newfunc

>>> print partial(pow, ..., 2, 3)(5) # (5^2)%3
1
>>> print partial(pow, 2, ..., 3)(5) # (2^5)%3
2
>>> print partial(pow, 2, 3, ...)(5) # (2^3)%5
3
>>> print partial(pow, 2, 3)(5) # (2^3)%5
3

So the the solution for the original question would be with this version of partial list(map(partial(pow, ..., 2),xs))

所以原始问题的解决方案将是这个版本的部分列表(map(partial(pow,...,2),xs))

#3


7  

Why not just create a quick lambda function which reorders the args and partial that

为什么不只是创建一个快速lambda函数,重新排序args和部分

partial(lambda p, x: pow(x, p), 2)

#4


6  

You could create a helper function for this:

您可以为此创建一个辅助函数:

from functools import wraps
def foo(a, b, c, d, e):
    print('foo(a={}, b={}, c={}, d={}, e={})'.format(a, b, c, d, e))

def partial_at(func, index, value):
    @wraps(func)
    def result(*rest, **kwargs):
        args = []
        args.extend(rest[:index])
        args.append(value)
        args.extend(rest[index:])
        return func(*args, **kwargs)
    return result

if __name__ == '__main__':
    bar = partial_at(foo, 2, 'C')
    bar('A', 'B', 'D', 'E') 
    # Prints: foo(a=A, b=B, c=C, d=D, e=E)

Disclaimer: I haven't tested this with keyword arguments so it might blow up because of them somehow. Also I'm not sure if this is what @wraps should be used for but it seemed right -ish.

免责声明:我没有用关键字参数对此进行测试,因此它可能会以某种方式爆炸。此外,我不确定这是否应该用@wraps,但似乎是正确的。

#5


5  

you could use a closure

你可以使用一个闭包

xs = [1,2,3,4,5,6,7,8]

def closure(method, param):
  def t(x):
    return method(x, param)
  return t

f = closure(pow, 2)
f(10)
f = closure(pow, 3)
f(10)

#6


2  

One way of doing it would be:

一种方法是:

def testfunc1(xs):
    from functools import partial
    def mypow(x,y): return x ** y
    return list(map(partial(mypow,y=2),xs))

but this involves re-defining the pow function.

但这涉及重新定义战俘功能。

if the use of partial was not 'needed' then a simple lambda would do the trick

如果不需要使用partial,那么一个简单的lambda就可以了

def testfunc2(xs):
    return list(map(lambda x: pow(x,2), xs))

And a specific way to map the pow of 2 would be

并且映射2的pow的特定方式将是

def testfunc5(xs):
    from operator import mul
    return list(map(mul,xs,xs))

but none of these fully address the problem directly of partial applicaton in relation to keyword arguments

但这些都没有完全解决与关键字参数有关的部分应用问题

#7


1  

You can do this with lambda, which is more flexible than functools.partial():

你可以用lambda做到这一点,它比functools.partial()更灵活:

pow_two = lambda base: pow(base, 2)
print(pow_two(3))  # 9

More generally:

def bind_skip_first(func, *args, **kwargs):
  return lambda first: func(first, *args, **kwargs)

pow_two = bind_skip_first(pow, 2)
print(pow_two(3))  # 9

One down-side of lambda is that some libraries are not able to serialize it.

lambda的一个缺点是某些库无法序列化它。

#8


1  

The very versatile funcy includes an rpartial function that exactly addresses this problem.

功能多样的功能包括一个完全解决这个问题的rpartial功能。

xs = [1,2,3,4,5,6,7,8]
from funcy import rpartial
list(map(rpartial(pow, 2), xs))
# [1, 4, 9, 16, 25, 36, 49, 64]

It's just a lambda under the hood:

它只是引擎盖下的一个lambda:

def rpartial(func, *args):
    """Partially applies last arguments."""
    return lambda *a: func(*(a + args))

#9


0  

As already said that's a limitation of functools.partial if the function you want to partial doesn't accept keyword arguments.

如前所述,如果你想要部分的函数不接受关键字参数,那就是functools.partial的限制。

If you don't mind using an external library 1 you could use iteration_utilities.partial which has a partial that supports placeholders:

如果您不介意使用外部库1,可以使用iteration_utilities.partial,它具有支持占位符的部分:

>>> from iteration_utilities import partial
>>> square = partial(pow, partial._, 2)  # the partial._ attribute represents a placeholder
>>> list(map(square, xs))
[1, 4, 9, 16, 25, 36, 49, 64]

1 Disclaimer: I'm the author of the iteration_utilities library (installation instructions can be found in the documentation in case you're interested).

1免责声明:我是iteration_utilities库的作者(如果您感兴趣,可以在文档中找到安装说明)。

#10


0  

If you can't use lambda functions, you can also write a simple wrapper function that reorders the arguments.

如果你不能使用lambda函数,你也可以编写一个简单的包装函数来重新排序参数。

def _pow(y, x):
    return pow(x, y)

and then call

然后打电话

list(map(partial(_pow,2),xs))

>>> [1, 4, 9, 16, 25, 36, 49, 64]

#1


28  

No

According to the documentation, partial cannot do this (emphasis my own):

根据文档,部分不能这样做(强调我自己):

partial.args

The leftmost positional arguments that will be prepended to the positional arguments

最左侧的位置参数将添加到位置参数之前


You could always just "fix" pow to have keyword args:

你可以随时“修复”pow以获得关键字args:

_pow = pow
pow = lambda x, y: _pow(x, y)

#2


11  

I think I'd just use this simple one-liner:

我想我只是使用这个简单的单线程:

import itertools
print list(itertools.imap(pow, [1, 2, 3], itertools.repeat(2)))

Update:

I also came up with a funnier than useful solution. It's a beautiful syntactic sugar, profiting from the fact that the ... literal means Ellipsis in Python3. It's a modified version of partial, allowing to omit some positional arguments between the leftmost and rightmost ones. The only drawback is that you can't pass anymore Ellipsis as argument.

我还提出了一个比有用的解决方案更有趣的方法。它是一个美丽的语法糖,从......文字意味着Python3中的省略号这一事实中获益。它是partial的修改版本,允许省略最左边和最右边的一些位置参数。唯一的缺点是你不能再将Ellipsis作为参数传递。

import itertools
def partial(func, *args, **keywords):
    def newfunc(*fargs, **fkeywords):
        newkeywords = keywords.copy()
        newkeywords.update(fkeywords)
        return func(*(newfunc.leftmost_args + fargs + newfunc.rightmost_args), **newkeywords)
    newfunc.func = func
    args = iter(args)
    newfunc.leftmost_args = tuple(itertools.takewhile(lambda v: v != Ellipsis, args))
    newfunc.rightmost_args = tuple(args)
    newfunc.keywords = keywords
    return newfunc

>>> print partial(pow, ..., 2, 3)(5) # (5^2)%3
1
>>> print partial(pow, 2, ..., 3)(5) # (2^5)%3
2
>>> print partial(pow, 2, 3, ...)(5) # (2^3)%5
3
>>> print partial(pow, 2, 3)(5) # (2^3)%5
3

So the the solution for the original question would be with this version of partial list(map(partial(pow, ..., 2),xs))

所以原始问题的解决方案将是这个版本的部分列表(map(partial(pow,...,2),xs))

#3


7  

Why not just create a quick lambda function which reorders the args and partial that

为什么不只是创建一个快速lambda函数,重新排序args和部分

partial(lambda p, x: pow(x, p), 2)

#4


6  

You could create a helper function for this:

您可以为此创建一个辅助函数:

from functools import wraps
def foo(a, b, c, d, e):
    print('foo(a={}, b={}, c={}, d={}, e={})'.format(a, b, c, d, e))

def partial_at(func, index, value):
    @wraps(func)
    def result(*rest, **kwargs):
        args = []
        args.extend(rest[:index])
        args.append(value)
        args.extend(rest[index:])
        return func(*args, **kwargs)
    return result

if __name__ == '__main__':
    bar = partial_at(foo, 2, 'C')
    bar('A', 'B', 'D', 'E') 
    # Prints: foo(a=A, b=B, c=C, d=D, e=E)

Disclaimer: I haven't tested this with keyword arguments so it might blow up because of them somehow. Also I'm not sure if this is what @wraps should be used for but it seemed right -ish.

免责声明:我没有用关键字参数对此进行测试,因此它可能会以某种方式爆炸。此外,我不确定这是否应该用@wraps,但似乎是正确的。

#5


5  

you could use a closure

你可以使用一个闭包

xs = [1,2,3,4,5,6,7,8]

def closure(method, param):
  def t(x):
    return method(x, param)
  return t

f = closure(pow, 2)
f(10)
f = closure(pow, 3)
f(10)

#6


2  

One way of doing it would be:

一种方法是:

def testfunc1(xs):
    from functools import partial
    def mypow(x,y): return x ** y
    return list(map(partial(mypow,y=2),xs))

but this involves re-defining the pow function.

但这涉及重新定义战俘功能。

if the use of partial was not 'needed' then a simple lambda would do the trick

如果不需要使用partial,那么一个简单的lambda就可以了

def testfunc2(xs):
    return list(map(lambda x: pow(x,2), xs))

And a specific way to map the pow of 2 would be

并且映射2的pow的特定方式将是

def testfunc5(xs):
    from operator import mul
    return list(map(mul,xs,xs))

but none of these fully address the problem directly of partial applicaton in relation to keyword arguments

但这些都没有完全解决与关键字参数有关的部分应用问题

#7


1  

You can do this with lambda, which is more flexible than functools.partial():

你可以用lambda做到这一点,它比functools.partial()更灵活:

pow_two = lambda base: pow(base, 2)
print(pow_two(3))  # 9

More generally:

def bind_skip_first(func, *args, **kwargs):
  return lambda first: func(first, *args, **kwargs)

pow_two = bind_skip_first(pow, 2)
print(pow_two(3))  # 9

One down-side of lambda is that some libraries are not able to serialize it.

lambda的一个缺点是某些库无法序列化它。

#8


1  

The very versatile funcy includes an rpartial function that exactly addresses this problem.

功能多样的功能包括一个完全解决这个问题的rpartial功能。

xs = [1,2,3,4,5,6,7,8]
from funcy import rpartial
list(map(rpartial(pow, 2), xs))
# [1, 4, 9, 16, 25, 36, 49, 64]

It's just a lambda under the hood:

它只是引擎盖下的一个lambda:

def rpartial(func, *args):
    """Partially applies last arguments."""
    return lambda *a: func(*(a + args))

#9


0  

As already said that's a limitation of functools.partial if the function you want to partial doesn't accept keyword arguments.

如前所述,如果你想要部分的函数不接受关键字参数,那就是functools.partial的限制。

If you don't mind using an external library 1 you could use iteration_utilities.partial which has a partial that supports placeholders:

如果您不介意使用外部库1,可以使用iteration_utilities.partial,它具有支持占位符的部分:

>>> from iteration_utilities import partial
>>> square = partial(pow, partial._, 2)  # the partial._ attribute represents a placeholder
>>> list(map(square, xs))
[1, 4, 9, 16, 25, 36, 49, 64]

1 Disclaimer: I'm the author of the iteration_utilities library (installation instructions can be found in the documentation in case you're interested).

1免责声明:我是iteration_utilities库的作者(如果您感兴趣,可以在文档中找到安装说明)。

#10


0  

If you can't use lambda functions, you can also write a simple wrapper function that reorders the arguments.

如果你不能使用lambda函数,你也可以编写一个简单的包装函数来重新排序参数。

def _pow(y, x):
    return pow(x, y)

and then call

然后打电话

list(map(partial(_pow,2),xs))

>>> [1, 4, 9, 16, 25, 36, 49, 64]