Aproximadamente, partial
hace algo como esto (aparte de la compatibilidad con argumentos de palabras clave, etc.):
def partial(func, *part_args):
def wrapper(*extra_args):
args = list(part_args)
args.extend(extra_args)
return func(*args)
return wrapper
Entonces, al llamar partial(sum2, 4)
, crea una nueva función (una llamada, para ser precisos) que se comporta como sum2
, pero tiene un argumento posicional menos. Ese argumento perdido siempre se sustituye por 4
, de modo quepartial(sum2, 4)(2) == sum2(4, 2)
En cuanto a por qué es necesario, hay una variedad de casos. Solo por uno, supongamos que tiene que pasar una función en algún lugar donde se espera que tenga 2 argumentos:
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)
Pero una función que ya tiene necesita acceso a algún tercer context
objeto para hacer su trabajo:
def log_event(context, event, params):
context.log_event("Something happened %s, %s", event, params)
Entonces, hay varias soluciones:
Un objeto personalizado:
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)
Con parciales:
context = get_context() # whatever
notifier.add_listener(partial(log_event, context))
De esos tres, partial
es el más corto y el más rápido. (Para un caso más complejo, es posible que desee un objeto personalizado).
extra_args
extra_args
es algo que pasó por el llamador parcial, en el ejemplo conp = partial(func, 1); f(2, 3, 4)
él es(2, 3, 4)
.callback
ymy_callback
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:
Para ordenar estos datos por distancia del objetivo, lo que le gustaría hacer, por supuesto, es esto:
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:p_euclid_dist
ahora acepta un solo argumento,así que ahora puede ordenar sus datos pasando la función parcial para el argumento clave del método de clasificación:
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.
crear una función parcial (usando la palabra clave arg)
También puede crear una función parcial con un argumento posicional
pero esto arrojará (por ejemplo, crear parciales con argumentos de palabras clave y luego llamar utilizando argumentos posicionales)
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: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:fuente
respuesta corta,
partial
da valores predeterminados a los parámetros de una función que de otro modo no tendría valores predeterminados.fuente
partial
y así sucesivamenteLos parciales se pueden usar para crear nuevas funciones derivadas que tengan algunos parámetros de entrada preasignados
Para ver el uso de parciales en el mundo real, consulte esta muy buena publicación de blog:
http://chriskiehl.com/article/Cleaner-coding-through-partially-applied-functions/
Un simple pero claro ejemplo de principiante del blog, cubiertas cómo se podrían utilizar
partial
enre.search
hacer el código más legible.re.search
La firma del método es:Al aplicar
partial
podemos crear múltiples versiones de la expresión regularsearch
para satisfacer nuestros requisitos, por ejemplo:Ahora
is_spaced_apart
yis_grouped_together
son dos nuevas funciones derivadas dere.search
que tienen elpattern
argumento aplicado (ya quepattern
es el primer argumento en lare.search
firma del método).La firma de estas dos nuevas funciones (invocables) es:
Así es como podría usar estas funciones parciales en algún texto:
Puede consultar el enlace de arriba para obtener una comprensión más profunda del tema, ya que cubre este ejemplo específico y mucho más.
fuente
is_spaced_apart = re.compile('[a-zA-Z]\s\=').search
? Si es así, ¿hay una garantía de que elpartial
idioma compila la expresión regular para una reutilización más rápida?En mi opinión, es una forma de implementar curry en python.
El resultado es 3 y 4.
fuente
También vale la pena mencionar que cuando una función parcial pasa a otra función en la que queremos "codificar" algunos parámetros, ese debería ser el parámetro más adecuado
pero si hacemos lo mismo, pero cambiando un parámetro en su lugar
arrojará el error, "TypeError: func () obtuvo múltiples valores para el argumento 'a'"
fuente
prt=partial(func, 7)
Esta respuesta es más un código de ejemplo. Todas las respuestas anteriores dan buenas explicaciones sobre por qué uno debe usar parcial. Daré mis observaciones y usaré casos sobre parcial.
La salida del código anterior debe ser:
Observe que en el ejemplo anterior se devolvió un nuevo invocable que tomará el parámetro (c) como argumento. Tenga en cuenta que también es el último argumento de la función.
La salida del código anterior también es:
Observe que * se usó para desempaquetar los argumentos que no son palabras clave y que el invocable devuelto en términos de qué argumento puede tomar es el mismo que el anterior.
Otra observación es: el siguiente ejemplo demuestra que parcial devuelve un invocable que tomará el parámetro no declarado (a) como argumento.
La salida del código anterior debe ser:
Similar,
Imprime el código anterior
Tuve que usarlo cuando estaba usando el
Pool.map_async
método delmultiprocessing
módulo. Puede pasar solo un argumento a la función de trabajador, por lo que tuve que usarpartial
para hacer que mi función de trabajo parezca invocable con solo un argumento de entrada, pero en realidad mi función de trabajador tenía múltiples argumentos de entrada.fuente