partials非常有用。
例如,在函数调用的“流水线”序列中(其中一个函数的返回值是传递给下一个函数的参数)。
有时,此类管道中的函数需要一个参数,但紧接其上游的函数会返回两个值。
在这种情况下,functools.partial
可能允许您保持此功能管道完好无损。
这是一个特定的孤立示例:假设您想按每个数据点与某个目标的距离对某些数据进行排序:
# create some data
import random as RND
fnx = lambda: RND.randint(0, 10)
data = [ (fnx(), fnx()) for c in range(10) ]
target = (2, 4)
import math
def euclid_dist(v1, v2):
x1, y1 = v1
x2, y2 = v2
return math.sqrt((x2 - x1)**2 + (y2 - y1)**2)
要按与目标的距离对这些数据进行排序,您当然想做的是:
data.sort(key=euclid_dist)
但你不能—— sort方法的key参数只接受带有单个参数的函数。
所以重写euclid_dist
为一个带单个参数的函数:
from functools import partial
p_euclid_dist = partial(euclid_dist, target)
p_euclid_dist
现在接受一个参数,
>>> p_euclid_dist((3, 3))
1.4142135623730951
所以现在你可以通过传入 sort 方法的 key 参数的偏函数来对数据进行排序:
data.sort(key=p_euclid_dist)
# verify that it works:
for p in data:
print(round(p_euclid_dist(p), 3))
1.0
2.236
2.236
3.606
4.243
5.0
5.831
6.325
7.071
8.602
或者例如,函数的参数之一在外部循环中发生变化,但在内部循环中的迭代期间是固定的。通过使用部分,您不必在内部循环的迭代期间传递附加参数,因为修改后的(部分)函数不需要它。
>>> from functools import partial
>>> def fnx(a, b, c):
return a + b + c
>>> fnx(3, 4, 5)
12
创建一个偏函数(使用关键字 arg)
>>> pfnx = partial(fnx, a=12)
>>> pfnx(b=4, c=5)
21
您还可以使用位置参数创建部分函数
>>> pfnx = partial(fnx, 12)
>>> pfnx(4, 5)
21
但这会抛出(例如,使用关键字参数创建部分然后使用位置参数调用)
>>> pfnx = partial(fnx, a=12)
>>> pfnx(4, 5)
Traceback (most recent call last):
File "<pyshell#80>", line 1, in <module>
pfnx(4, 5)
TypeError: fnx() got multiple values for keyword argument 'a'
multiprocessing
另一个用例:使用 python 的库编写分布式代码。使用 Pool 方法创建进程池:
>>> import multiprocessing as MP
>>> # create a process pool:
>>> ppool = MP.Pool()
Pool
有一个 map 方法,但它只需要一个可迭代的,所以如果你需要传入一个具有更长参数列表的函数,请将函数重新定义为部分函数,以修复除一个之外的所有函数:
>>> ppool.map(pfnx, [4, 6, 7, 8])