2012-04-27 22 views
5

This answer explica cómo crear casos de prueba dinámicamente.¿Qué hace el auto lambda: do

código de la respuesta:

class Tests(unittest.TestCase): 
    def check(self, i, j): 
        self.assertNotEquals(0, i-j) 

for i in xrange(1, 4): 
    for j in xrange(2, 6): 
        def ch(i, j): 
            return lambda self: self.check(i, j) 
        setattr(Tests, "test_%r_%r" % (i, j), ch(i, j)) 

que he probado y funciona, pero no puedo averiguar cómo ?

tengo problemas para entender la magia lambda self: en juego aquí, principalmente:

  1. ¿Está la lambda se usa aquí para realizar exactamente lo contrario de functools.partial() (es decir, para crear una función de contenedor con una parámetro adicional que es aún no se conoce)
  2. ¿Es self una palabra clave significativa o lambda spam funcionaría igual de bien?
  3. ¿Qué punto es que se evaluó lambda?
  4. ¿Por qué el .check() está perfectamente bien fuera del alcance class?
  5. ¿Cómo se hace esto sin lambda? - si he entendido bien, you should be able to do without lambda (por no hablar de que estoy de acuerdo con Guido y Alex que es desagradables a la vista y yo quiero que prescindir :)

Respuesta

3

En primer lugar: El lambda es devuelto desde la función de evitar que los valores modificados de i y j se utilizará más adelante.

Compare con lo siguiente:

for i in xrange(1, 4): 
    for j in xrange(2, 6): 
     setattr(Tests, "test_%r_%r" % (i, j), lambda self: self.check(i, j)) 

esto no funcionará como se esperaba, ya que todas las lambdas compartirán el mismo espacio de nombres y utilizarán los valores de ij y después del final del bucle. Si utilizamos una llamada de función separada, presentamos un nuevo cierre cada vez que captura los valores de i y j en el momento en que se llama a la función.

Podríamos lograr lo mismo guardando los valores actuales de i y j como argumentos predeterminados de la lambda. Por si fuera poco, también vamos a utilizar itertools.product para evitar el bucle anidado:

from itertools import product 
from operator import methodcaller 

for i, j in product(range(1, 4), range(2, 6)): 
    setattr(Tests, "test_%r_%r" % (i, j), 
      lambda self, i=i, j=j: self.check(i, j)) 

Is the lambda used here to perform the exact opposite of functools.partial() (i.e. to create a wrapper function with one extra parameter that is not yet known)

No

realmente. Simplemente llama al método check(i, j) en lo que se da como argumento. Esto se usa aquí para agregar dinámicamente métodos a la clase Tests.

Is self a meaningful keyword or would lambda spam would work just as well?

spam funcionaría igual de bien. Eligen self aquí debido a la convención porque el lambda representa un método.

What point is that lambda evaluated?

Tan pronto como test_[i]_[j]() se llama en una instancia de Tests.

How come the .check() is perfectly fine outside the classes scope?

Debido a que hay dentro de una única lambda se llamará más tarde con una instancia de Testsself como argumento.

+1

ok "No realmente. Simplemente llama al método check (i, j) sobre lo que se da como argumento. Esto se usa aquí para agregar métodos dinámicamente a la clase Tests". fue lo que no me di cuenta. Ahora que lo mencionas, es evidente (juego de palabras) – Kimvais

+0

La modificación methodcaller (que también haría el código más legible, IMO) me da 'TypeError: methodcaller esperaba 1 argumentos, obtuvo 0' – Kimvais

+0

@Kimvais: Esto es muy extraño. Parece que la función no está vinculada correctamente cuando se asigna al atributo de clase. Esto podría deberse al hecho de que no es una lambda real, sino una función definida en el lado C de Python. Supongo que abriré una pregunta por separado para eso. –

3

lambda devuelve una función. self es su argumento, y I y J son encerrados dentro de ella

Así, ch (i, j) es una función de iyj, que devuelve una función de auto.

self no tiene ningún significado mágico aquí como nada en Python - cada variable y función son explícitas excepto por algunos built-ins. Pero tan pronto como estos métodos se apegan a la clase, el yo se convierte en su primer argumento.

Esto significa

  • después del bucle La clase Análisis consigue múltiples métodos llamados check_i_j

  • cada uno de ellos tiene valores de i y j encerrado en ella

  • cada uno de ellos tiene su primer param llamado self que es bueno porque están definidos dentro de la clase, son métodos de instancia, y por lo tanto su primer parámetro debería llamarse se LF por convención

+0

Por cierto, el código podría simplificarse mucho escribiendo solo setattr (Pruebas, "test_% r_% r"% (i, j), lambda self: auto.check (i, j)) – Guard

+0

No, eso hace algo completamente diferente. –

+0

@NiklasB. por favor explique: ¿cuál es la diferencia entre definir la función ch e invocarla instantáneamente y simplemente poner su valor? i y j están encerrados de todos modos, ¿verdad? – Guard

2

a_function = lambda x: do_something_with(x)

es equivalente a

def a_function(x): 
    return do_something_with(x) 

En su ejemplo particular, las funciones creadas son métodos de instancia. Así, por ejemplo, usted podría escribir:

class Tests: 
... 
    test_1_2 = lambda self: self.check(1, 2) 

lo que sería equivalente a:

class Tests: 
... 
    def test_1_2(self): 
     return self.check(1, 2) 

Ahora, ya que los identificadores se generan de forma dinámica, por lo tanto, utilizar de setattr.


  1. Is the lambda used here to perform the exact opposite of functools.partial() (i.e. to create a wrapper function with one extra parameter that is not yet known)

No, no realmente. De hecho, partial podría usarse aquí también.

test_1_2 = lambda self: self.check(1, 2) 

habría convirtió

primer argumento
test_1_2 = partial(Tests.check, i=1, j=2) 
  1. Is self a meaningful keyword or would lambda spam would work just as well?

del método de instancia siempre tiene que ser el caso. La convención es para nombrar ese parámetro self, pero puede usar cualquier otro nombre.No lo aconsejaría, ya que hará que su código sea más difícil de leer y comprender.

  1. What point is that lambda evaluated?

lambda crea función anónima, que como cualquier función se evalúa mediante convocatoria.

  1. How come the .check() is perfectly fine outside the classes scope?

No lo es. Es self.check(), donde self es una instancia de la clase Tests.

+2

En realidad, 'operator.methodcaller (" check ", 1, 2)' es preferible a 'partial' aquí porque usa el tipado de pato y no requiere una instancia de' Tests'. Este comentario también se aplica a su respuesta a la otra pregunta. –

+0

@NiklasB .: ok, pero la pregunta era si 'partial' es algo opuesto a' lambda', no si es la mejor manera – vartec

+0

Claro, tal vez debería haberlo agregado a tu otra respuesta. –