los parciales son increíblemente útiles.
Por ejemplo, en una secuencia de llamadas de función 'en línea' (en la que el valor devuelto de una función es el argumento pasado a la siguiente).
A veces, una función en una tubería de este tipo requiere un único argumento , pero la función inmediatamente aguas arriba devuelve dos valores .
En este escenario, functools.partial
podría permitirle mantener intacta esta canalización de funciones.
Aquí hay un ejemplo específico y aislado: suponga que desea ordenar algunos datos por la distancia de cada punto de datos desde algún objetivo:
# 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)
Para ordenar estos datos por distancia del objetivo, lo que le gustaría hacer, por supuesto, es esto:
data.sort(key=euclid_dist)
pero no puede: el parámetro clave del método de clasificación solo acepta funciones que toman un solo argumento.
reescriba euclid_dist
como una función tomando un solo parámetro:
from functools import partial
p_euclid_dist = partial(euclid_dist, target)
p_euclid_dist
ahora acepta un solo argumento,
>>> p_euclid_dist((3, 3))
1.4142135623730951
así que ahora puede ordenar sus datos pasando la función parcial para el argumento clave del método de clasificació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
O, por ejemplo, uno de los argumentos de la función cambia en un bucle externo, pero se repara durante la iteración en el bucle interno. Al usar un parcial, no tiene que pasar el parámetro adicional durante la iteración del bucle interno, porque la función modificada (parcial) no lo requiere.
>>> from functools import partial
>>> def fnx(a, b, c):
return a + b + c
>>> fnx(3, 4, 5)
12
crear una función parcial (usando la palabra clave arg)
>>> pfnx = partial(fnx, a=12)
>>> pfnx(b=4, c=5)
21
También puede crear una función parcial con un argumento posicional
>>> pfnx = partial(fnx, 12)
>>> pfnx(4, 5)
21
pero esto arrojará (por ejemplo, crear parciales con argumentos de palabras clave y luego llamar utilizando argumentos posicionales)
>>> 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'
otro caso de uso: escribir código distribuido usando la multiprocessing
biblioteca de Python . Se crea un grupo de procesos utilizando el método Pool:
>>> import multiprocessing as MP
>>> # create a process pool:
>>> ppool = MP.Pool()
Pool
tiene un método de mapa, pero solo toma un único iterable, por lo que si necesita pasar una función con una lista de parámetros más larga, redefina la función como parcial, para arreglar todos menos uno:
>>> ppool.map(pfnx, [4, 6, 7, 8])
extra_args