52

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

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]

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

要得到[1, 4, 9, 16, 25, 49, 64]

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

TypeError: pow() takes no keyword arguments

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

4

12 回答 12

51

根据文档不能这样做(强调我自己的):partial

部分参数

将附加到位置参数的最左边的位置参数


你总是可以“修复”pow关键字参数:

_pow = pow
pow = lambda x, y: _pow(x, y)
于 2012-06-23T23:29:29.587 回答
19

我想我会使用这个简单的单线:

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

更新:

我还想出了一个比有用的解决方案更有趣的解决方案。这是一个漂亮的语法糖,受益于 Python3中的...字面意思。Ellipsis它是 的修改版本partial,允许省略最左边和最右边之间的一些位置参数。唯一的缺点是您不能再将省略号作为参数传递。

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

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

于 2012-06-23T23:43:03.747 回答
17

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

partial(lambda p, x: pow(x, p), 2)
于 2016-05-26T18:12:15.030 回答
8

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

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)

免责声明:我没有用关键字参数测试过这个,所以它可能会因为它们而爆炸。另外我不确定这是否@wraps应该用于,但它似乎是正确的-ish。

于 2012-06-23T23:10:46.610 回答
4

你可以使用闭包

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)
于 2012-06-23T23:16:55.200 回答
3

您可以使用 执行此操作lambda,它比 更灵活functools.partial()

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

更普遍:

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

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

于 2018-02-27T18:57:56.143 回答
2

一种方法是:

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

但这涉及重新定义 pow 函数。

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

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

映射 2 的战俘的具体方法是

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

但是这些都没有直接解决与关键字参数相关的部分应用问题

于 2012-06-23T22:59:28.917 回答
1

如前所述,functools.partial如果您想要的函数partial不接受关键字参数,这是一个限制。

如果您不介意使用外部库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免责声明:我是该库的作者(如果您有兴趣,iteration_utilities可以在文档中找到安装说明)。

于 2017-06-24T10:00:31.137 回答
1

非常通用的功能包括一个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]

它只是引擎盖下的 lambda:

def rpartial(func, *args):
    """Partially applies last arguments."""
    return lambda *a: func(*(a + args))
于 2018-08-13T19:32:59.487 回答
1

即使已经回答了这个问题,您也可以使用以下食谱获得您正在寻找的结果itertools.repeat

from itertools import repeat


xs = list(range(1, 9))  # [1, 2, 3, 4, 5, 6, 7, 8]
xs_pow_2 = list(map(pow, xs, repeat(2)))  # [1, 4, 9, 16, 25, 36, 49, 64]

希望这可以帮助某人。

于 2020-12-28T18:17:47.487 回答
0

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

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

然后打电话

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

>>> [1, 4, 9, 16, 25, 36, 49, 64]
于 2018-09-14T13:03:16.123 回答
0

是的

如果您创建了部分类

class MyPartial:
    def __init__(self, func, *args):
        self._func = func
        self._args = args
        
    def __call__(self, *args):
        return self._func(*args, *self._args) # swap ordering
    
xs = [1,2,3,4,5,6,7,8]
list(map(MyPartial(pow,2),xs))

>>> [1, 4, 9, 16, 25, 36, 49, 64]
于 2021-08-26T20:02:32.943 回答