以内置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]
但是我如何将 XS 提高到 2 的幂?
获得[1, 4, 9, 16, 25, 49, 64]
list(map(partial(pow,y=2),xs))
TypeError: pow() takes no keyword arguments
我知道列表理解会更容易。
否
根据文档,partial
不能这样做(强调我自己的):
<小时 />部分.args
将附加到位置参数的最左侧位置参数
您始终可以"修复"pow
以使用关键字参数:
_pow = pow
pow = lambda x, y: _pow(x, y)
我想我只会使用这个简单的单行:
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))
为什么不创建一个快速的lambda函数来重新排序args并部分
partial(lambda p, x: pow(x, p), 2)
您可以为此创建一个帮助程序函数:
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。
你可以使用闭包
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)
用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 的一个缺点是某些库无法序列化它。
一种方法是:
def testfunc1(xs):
from functools import partial
def mypow(x,y): return x ** y
return list(map(partial(mypow,y=2),xs))
但这涉及重新定义 PoW 函数。
如果"不需要"使用部分,那么一个简单的 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))
但这些都没有完全解决与关键字参数相关的部分应用问题
即使这个问题已经回答了,您也可以使用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]
希望这对某人有所帮助。
是的,你可以这样做,只要函数接受关键字参数。你只需要知道名字。
对于pow()
(前提是您使用的是Python 3.8或更高版本),则需要exp
而不是y。
尝试做:
xs = [1,2,3,4,5,6,7,8]
print(list(map(partial(pow,exp=2),xs)))
前所述,如果您要partial
的函数不接受关键字参数,则这是functools.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
库的作者(如果您有兴趣,可以在文档中找到安装说明)。
非常通用的功能包括一个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]
这只是引擎盖下的λ:
def rpartial(func, *args):
"""Partially applies last arguments."""
return lambda *a: func(*(a + args))
如果您不能使用 lambda 函数,您还可以编写一个简单的包装函数来重新排序参数。
def _pow(y, x):
return pow(x, y)
然后致电
list(map(partial(_pow,2),xs))
>>> [1, 4, 9, 16, 25, 36, 49, 64]
是
如果创建了分部类
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]