parziali sono incredibilmente utili.
Ad esempio, in una sequenza di chiamate di funzioni "allineate" (in cui il valore restituito da una funzione è l'argomento passato alla successiva).
A volte una funzione in tale pipeline richiede un singolo argomento , ma la funzione immediatamente a monte da essa restituisce due valori .
In questo scenario, functools.partial
potrebbe consentire di mantenere intatta questa pipeline di funzioni.
Ecco un esempio specifico e isolato: supponiamo di voler ordinare alcuni dati in base alla distanza di ciascun punto dati da un obiettivo:
# 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)
Per ordinare questi dati in base alla distanza dal bersaglio, quello che vorresti fare ovviamente è questo:
data.sort(key=euclid_dist)
ma non puoi: il parametro chiave del metodo di ordinamento accetta solo funzioni che accettano un singolo argomento.
quindi riscrivi euclid_dist
come una funzione prendendo un singolo parametro:
from functools import partial
p_euclid_dist = partial(euclid_dist, target)
p_euclid_dist
ora accetta un singolo argomento,
>>> p_euclid_dist((3, 3))
1.4142135623730951
quindi ora puoi ordinare i tuoi dati passando la funzione parziale per l'argomento chiave del metodo di ordinamento:
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
O ad esempio, uno degli argomenti della funzione cambia in un ciclo esterno ma viene corretto durante l'iterazione nel ciclo interno. Usando un parziale, non è necessario passare il parametro aggiuntivo durante l'iterazione del ciclo interno, poiché la funzione modificata (parziale) non lo richiede.
>>> from functools import partial
>>> def fnx(a, b, c):
return a + b + c
>>> fnx(3, 4, 5)
12
creare una funzione parziale (usando la parola chiave arg)
>>> pfnx = partial(fnx, a=12)
>>> pfnx(b=4, c=5)
21
puoi anche creare una funzione parziale con un argomento posizionale
>>> pfnx = partial(fnx, 12)
>>> pfnx(4, 5)
21
ma questo genererà (ad esempio, la creazione di un argomento parziale con l'argomento parola chiave, quindi la chiamata mediante argomenti posizionali)
>>> 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'
un altro caso d'uso: scrivere codice distribuito usando la multiprocessing
libreria di Python . Un pool di processi viene creato utilizzando il metodo Pool:
>>> import multiprocessing as MP
>>> # create a process pool:
>>> ppool = MP.Pool()
Pool
ha un metodo map, ma richiede solo un singolo iterabile, quindi se è necessario passare una funzione con un elenco di parametri più lungo, ridefinire la funzione come parziale, per correggere tutti tranne uno:
>>> ppool.map(pfnx, [4, 6, 7, 8])
extra_args
variabile