2010-12-06 13 views
8

Muy raramente me encontraré con algún código en python que utiliza una función anónima que devuelve una función anónima ...?lambda devuelve lambda en python

Lamentablemente no puedo encontrar un ejemplo en la mano, pero por lo general toma la forma de esta manera:

g = lambda x,c: x**c lambda c: c+1 

Por qué alguien haría esto? Tal vez puedas dar un ejemplo que tenga sentido (no estoy seguro de que el que hice tenga sentido).

Edición: He aquí un ejemplo:

swap = lambda a,x,y:(lambda f=a.__setitem__:(f(x,(a[x],a[y])), 
     f(y,a[x][0]),f(x,a[x][1])))() 
+3

buscar y mostrar una ejemplo real; ese no es sintácticamente correcto. –

+0

produce SyntaxError: sintaxis no válida. No tiene sentido second lambda – joaquin

+0

¿Por qué alguien haría esto? Respuesta: ¿Irritar a los demás? – joaquin

Respuesta

13

se puede utilizar una construcción de este tipo de hacer currying:

curry = lambda f, a: lambda x: f(a, x) 

Usted puede usarlo como:

>>> add = lambda x, y: x + y 
>>> add5 = curry(add, 5) 
>>> add5(3) 
8 
+0

esto no se hace en general mejor con functools.partial? – joaquin

+0

@joaquin: Sí. Solo estoy presentando un ejemplo. –

1

Se puede ser útil para marcadores de posición temporales. Suponga que tiene una fábrica de decorador:

@call_logger(log_arguments=True, log_return=False) 
def f(a, b): 
    pass 

Puede reemplazar de manera transitoria con

call_logger = lambda *a, **kw: lambda f: f 

También puede ser útil si indirectamente devuelve una lambda:

import collections 
collections.defaultdict(lambda: collections.defaultdict(lambda: collections.defaultdict(int))) 

También es útil para creando fábricas llamables en la consola de Python.

Y solo porque algo es posible no significa que tenga que usarlo.

1

Esto se puede utilizar para extraer algún código repetitivo común (hay, por supuesto, otras formas de lograr esto en python).

Tal vez esté escribiendo un registrador, y debe anteponer el nivel a la cadena de registro. Es posible escribir algo como:

import sys 
prefixer = lambda prefix: lambda message: sys.stderr.write(prefix + ":" + message + "\n") 
log_error = prefixer("ERROR") 
log_warning = prefixer("WARNING") 
log_info = prefixer("INFO") 
log_debug = prefixer("DEBUG") 

log_info("An informative message") 
log_error("Oh no, a fatal problem") 

Este programa imprime

INFO:An informative message 
    ERROR:Oh no, a fatal problem 
1

hice algo como esto el otro día para desactivar un método de prueba en una suite unittest.

disable = lambda fn : lambda *args, **kwargs: None 

@disable 
test_method(self): 
    ... test code that I wanted to disable ... 

Fácil de volver a habilitar después.

2
swap = lambda a,x,y:(lambda f=a.__setitem__:(f(x,(a[x],a[y])), 
     f(y,a[x][0]),f(x,a[x][1])))() 

Ver el() al final? La lambda interna no se devuelve, se llama.

la función hace el equivalente de

def swap(a, x, y): 
    a[x] = (a[x], a[y]) 
    a[y] = a[x][0] 
    a[x] = a[x][1] 

Pero supongamos que queremos hacer esto de una lambda. No podemos usar asignaciones en un lambda. Sin embargo, podemos llamar al __setitem__ para obtener el mismo efecto.

def swap(a, x, y): 
    a.__setitem__(x, (a[x], a[y])) 
    a.__setitem__(y, a[x][0]) 
    a.__setitem__(x, a[x][1]) 

Pero para una lambda, solo podemos tener una expresión. Pero ya que estas son las llamadas a funciones que podemos envolverlos en una tupla

def swap(a, x, y): 
    (a.__setitem__(x, (a[x], a[y])), 
    a.__setitem__(y, a[x][0]), 
    a.__setitem__(x, a[x][1])) 

Sin embargo, todos esos __setitem__ 's me están consiguiendo abajo, así que vamos a factorizar a cabo:

def swap(a, x, y): 
    f = a.__setitem__ 
    (f(x, (a[x], a[y])), 
    f(y, a[x][0]), 
    f(x, a[x][1])) 

Dagnamit, que pueda ¡No salgas con la tarea de agregar otra tarea! Sé que vamos a abusar de los parámetros predeterminados.

def swap(a, x, y): 
    def inner(f = a.__setitem__): 
     (f(x, (a[x], a[y])), 
     f(y, a[x][0]), 
     f(x, a[x][1])) 
    inner() 

Ok vamos a cambiar a lambdas:

swap = lambda a, x, y: lambda f = a.__setitem__: (f(x, (a[x], a[y])), f(y, a[x][0]), f(x, a[x][1]))() 

Lo que nos lleva de nuevo a la expresión original (más/menos errores tipográficos)

Todo esto lleva de nuevo a la pregunta: ¿Por qué?

La función debería haberse aplicado como

def swap(a, x, y): 
    a[x],a[y] = a[y],a[x] 

El autor original fue salir de su manera de utilizar una lambda en lugar de una función. Podría ser que, por alguna razón, no le gusta la función anidada. No lo sé. Todo lo que diré es su código malo. (a menos que haya una misteriosa justificación para ello)

0

Lo más frecuente es que al menos en el código que encuentro y que yo mismo escribo solía "congelar" una variable con el valor que tiene en el punto en que la lambda la función es creada. De lo contrario, la variable nonlocals hace referencia a una variable en el alcance que existen, lo que a veces puede generar resultados no deseados.

Por ejemplo, si quiero crear una lista de diez funciones, siendo un multiplicador para un escalar de 0 a 9. Una cada uno podría estar tentado a escribir así:

>>> a = [(lambda j: i * j) for i in range(10)] 
>>> a[9](10) 
90 

El que, Si desea utilizar cualquiera de las otras funciones factoried se obtiene el mismo resultado:

>>> a[1](10) 
90 

Eso es debido a que la variable "i" en el interior del lambda no se resuelve cuando se crea la lambda. Por el contrario, Python mantiene una referencia a la "i" en la declaración "para", en el ámbito en que se creó (esta referencia se mantiene en el cierre de la función lambda). Cuando se ejecuta el lambda, la variable se evalúa y su valor es el último que tenía en ese alcance.

Cuando uno utiliza dos lambdas anidados como este:

>>> a = [(lambda k: (lambda j: k * j))(i) for i in range(10)] 

La variable "i" se evalúa DURINT la ejecución del bucle "for". Su valor se pasa a "k" - y se usa "k" como la variable no local en la función multiplicadora que estamos factorizando. Para cada valor de i, habrá una instancia diferente de la función lambda adjunta, y un valor diferente para la variable "k".

lo tanto, es posible alcanzar el objetivo original:

>>> a = [(lambda k: (lambda j: k * j))(i) for i in range(10)] 
>>> a[1](10) 
10 
>>> a[9](10) 
90 
>>> 
+0

¿Por qué no usar: a = [(lambda k, i = i: i * j) para j en el rango (10)]? –