Aproximadamente, partialhace 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 contextobjeto 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, partiales el más corto y el más rápido. (Para un caso más complejo, es posible que desee un objeto personalizado).
extra_argsextra_argses algo que pasó por el llamador parcial, en el ejemplo conp = partial(func, 1); f(2, 3, 4)él es(2, 3, 4).callbackymy_callbacklos 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.partialpodrí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_distcomo una función tomando un solo parámetro:p_euclid_distahora 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
multiprocessingbiblioteca de Python . Se crea un grupo de procesos utilizando el método Pool:Pooltiene 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,
partialda valores predeterminados a los parámetros de una función que de otro modo no tendría valores predeterminados.fuente
partialy 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
partialenre.searchhacer el código más legible.re.searchLa firma del método es:Al aplicar
partialpodemos crear múltiples versiones de la expresión regularsearchpara satisfacer nuestros requisitos, por ejemplo:Ahora
is_spaced_apartyis_grouped_togetherson dos nuevas funciones derivadas dere.searchque tienen elpatternargumento aplicado (ya quepatternes el primer argumento en lare.searchfirma 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 elpartialidioma 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_asyncmétodo delmultiprocessingmódulo. Puede pasar solo un argumento a la función de trabajador, por lo que tuve que usarpartialpara 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