3 回答

TA貢獻(xiàn)1856條經(jīng)驗(yàn) 獲得超5個(gè)贊
大致地,partial做這樣的事情(除了關(guān)鍵字args支持等):
def partial(func, *part_args):
def wrapper(*extra_args):
args = list(part_args)
args.extend(extra_args)
return func(*args)
return wrapper
因此,通過(guò)調(diào)用partial(sum2, 4)您可以創(chuàng)建一個(gè)行為類似于的新函數(shù)(準(zhǔn)確地說(shuō)是一個(gè)可調(diào)用的函數(shù))sum2,但位置參數(shù)要少一個(gè)。缺少的參數(shù)始終由代替4,因此partial(sum2, 4)(2) == sum2(4, 2)
至于為什么需要它,有很多種情況。僅作為一個(gè)例子,假設(shè)您必須在一個(gè)預(yù)期有2個(gè)參數(shù)的地方傳遞一個(gè)函數(shù):
class EventNotifier(object):
def __init__(self):
self._listeners = []
def add_listener(self, callback):
''' callback should accept two positional arguments, event and params '''
self._listeners.append(callback)
# ...
def notify(self, event, *params):
for f in self._listeners:
f(event, params)
但是您已經(jīng)擁有的功能需要訪問(wèn)某些第三context對(duì)象才能完成其工作:
def log_event(context, event, params):
context.log_event("Something happened %s, %s", event, params)
因此,有幾種解決方案:
自定義對(duì)象:
class Listener(object):
def __init__(self, context):
self._context = context
def __call__(self, event, params):
self._context.log_event("Something happened %s, %s", event, params)
notifier.add_listener(Listener(context))
Lambda:
log_listener = lambda event, params: log_event(context, event, params)
notifier.add_listener(log_listener)
帶有局部:
context = get_context() # whatever
notifier.add_listener(partial(log_event, context))
在這三個(gè)中,partial最短和最快。(對(duì)于更復(fù)雜的情況,您可能需要自定義對(duì)象)。

TA貢獻(xiàn)1836條經(jīng)驗(yàn) 獲得超5個(gè)贊
局部函數(shù)非常有用。
例如,在“管線式”函數(shù)調(diào)用序列中(其中一個(gè)函數(shù)的返回值是傳遞給下一個(gè)函數(shù)的參數(shù))。
有時(shí),這樣的管道中的函數(shù)需要單個(gè)參數(shù),但是緊接其上游的函數(shù)返回兩個(gè)值。
在這種情況下,functools.partial可能允許您保持此功能管道完整。
這是一個(gè)特定的隔離示例:假設(shè)您要按每個(gè)數(shù)據(jù)點(diǎn)與目標(biāo)之間的距離對(duì)一些數(shù)據(jù)進(jìn)行排序:
# 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)
要按距目標(biāo)的距離對(duì)這些數(shù)據(jù)進(jìn)行排序,您當(dāng)然要做的是:
data.sort(key=euclid_dist)
但你不可阻擋-的排序方法的關(guān)鍵參數(shù),只接受拍攝功能單一的參數(shù)。
因此,請(qǐng)改寫euclid_dist為帶有單個(gè)參數(shù)的函數(shù):
from functools import partial
p_euclid_dist = partial(euclid_dist, target)
p_euclid_dist 現(xiàn)在接受一個(gè)參數(shù),
>>> p_euclid_dist((3, 3))
1.4142135623730951
因此,現(xiàn)在您可以通過(guò)傳遞sort方法的key參數(shù)的部分函數(shù)來(lái)對(duì)數(shù)據(jù)進(jìn)行排序:
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
又例如,函數(shù)的參數(shù)之一在外循環(huán)中更改,但在內(nèi)循環(huán)的迭代過(guò)程中是固定的。通過(guò)使用部分函數(shù),您無(wú)需在內(nèi)部循環(huán)的迭代過(guò)程中傳遞其他參數(shù),因?yàn)樾薷暮蟮模ú糠趾瘮?shù))不需要此參數(shù)。
>>> from functools import partial
>>> def fnx(a, b, c):
return a + b + c
>>> fnx(3, 4, 5)
12
創(chuàng)建一個(gè)局部函數(shù)(使用關(guān)鍵字arg)
>>> pfnx = partial(fnx, a=12)
>>> pfnx(b=4, c=5)
21
您還可以使用位置參數(shù)創(chuàng)建部分函數(shù)
>>> pfnx = partial(fnx, 12)
>>> pfnx(4, 5)
21
但這會(huì)拋出(例如,創(chuàng)建帶有關(guān)鍵字參數(shù)的partial,然后使用位置參數(shù)調(diào)用)
>>> 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'
另一個(gè)用例:使用python的multiprocessing庫(kù)編寫分布式代碼。使用Pool方法創(chuàng)建一個(gè)進(jìn)程池:
>>> import multiprocessing as MP
>>> # create a process pool:
>>> ppool = MP.Pool()
Pool 有一個(gè)map方法,但是它只需要一個(gè)可迭代的方法,因此,如果您需要傳入帶有較長(zhǎng)參數(shù)列表的函數(shù),請(qǐng)將該函數(shù)重新定義為局部函數(shù),以修復(fù)除一個(gè)以外的所有函數(shù):
>>> ppool.map(pfnx, [4, 6, 7, 8])
添加回答
舉報(bào)