2012-04-27 12 views
5

This answer spiega come creare i casi di test in modo dinamico.Cosa fa il lambda: do

codice della risposta:

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)) 

ho provato e funziona, ma non riesco proprio a capire come ?

ho difficoltà a capire il lambda self: magia in gioco qui, principalmente:

  1. È lambda usata qui per effettuare l'esatto opposto di functools.partial() (cioè per creare una funzione wrapper con un più parametro che è non ancora noto)
  2. È self una parola chiave significativa o sarebbe lambda spam funzionerebbe altrettanto bene?
  3. Che punto è valutato Lambda?
  4. Come mai il .check() sta perfettamente bene fuori dall'ambito class es?
  5. Come si fa questo senza lambda? - se ho capito bene, you should be able to do without lambda (per non parlare che sono d'accordo con Guido e Alex che è un pugno nell'occhio e mi voglio fare a meno :)

risposta

3

Innanzitutto: Il lambda viene restituita da una funzione di impedire che i valori modificati i e j verrà utilizzata successivamente.

confronta con il seguente:

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)) 

questo non funzionerà come previsto, come tutte le lambda condivideranno lo stesso spazio dei nomi e useranno i valori di i e j dopo la fine del ciclo. Se utilizziamo una chiamata di funzione separata, introduciamo una nuova chiusura ogni volta che acquisisce i valori di i e j nel momento in cui viene chiamata la funzione.

Potremmo ottenere lo stesso salvando i valori correnti di i e j come argomenti predefiniti del lambda. Per buona misura, mettiamoci anche usare itertools.product per evitare il ciclo nidificato:

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)

Non

davvero. Chiama semplicemente il metodo check(i, j) su qualunque argomento venga fornito come argomento. Questo è usato qui per aggiungere dinamicamente metodi alla classe Tests.

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

spam funzionerebbe altrettanto bene. Scelgono self qui per convenzione perché il lambda rappresenta un metodo.

What point is that lambda evaluated?

Appena test_[i]_[j]() è chiamato in un'istanza di Tests.

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

Perché è all'interno di un lambda sarà chiamato solo successivamente con un'istanza Tests come argomento self.

+1

ok "Non proprio, chiama solo il metodo check (i, j) su qualunque argomento viene fornito come argomento, utilizzato qui per aggiungere dinamicamente i metodi alla classe Tests." era la cosa che non avevo capito. Ora che ne parli, è ovvio (gioco di parole) – Kimvais

+0

La modifica di methodcaller (che renderebbe anche il codice più leggibile, IMO) mi dà 'TypeError: methodcaller ha previsto 1 argomenti, ottenuto 0' – Kimvais

+0

@Kimvais: Questo è molto strano. Sembra che la funzione non sia legata correttamente quando assegnata all'attributo class. Ciò potrebbe essere dovuto al fatto che non è un vero lambda, ma una funzione definita sul lato C di Python. Immagino che aprirò una domanda a parte per quello. –

3

lambda restituisce una funzione. self è la sua tesi, e i e j sono racchiusi all'interno di esso

Quindi, ch (i, j) è una funzione di i e j che restituisce una funzione di .

auto non ha alcun significato magico qui come nulla in Python - ogni variabile e funzione sono esplicite ad eccezione di alcuni built-in. Ma non appena questi metodi sono collegati alla classe, il sé diventa il loro primo argomento.

Ciò significa

  • dopo il ciclo La classe Test ottiene diversi metodi chiamati check_i_j

  • ognuno di essi ha valori i e j accluso in esso

  • ciascuno di essi ha il loro primo parametro chiamato self che è buono perché sono definiti all'interno della classe, sono metodi di istanza e quindi il loro primo parametro dovrebbe essere chiamato se ln per convenzione

+0

BTW, il codice potrebbe essere molto semplificato semplicemente scrivendo setattr (Test, "test_% r_% r"% (i, j), lambda self: self.check (i, j)) – Guard

+0

No, che fa qualcosa di completamente diverso. –

+0

@NiklasB. per favore spiega - qual è la differenza tra definire la funzione ch e chiamarla all'istante e metterla a valore. io e j siamo chiusi comunque, giusto? – Guard

2

a_function = lambda x: do_something_with(x)

è equivalente a

def a_function(x): 
    return do_something_with(x) 

Nel vostro esempio particolare, le funzioni create sono metodi di istanza. Così per esempio si potrebbe scrivere:

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

che sarebbe equivalente a:

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

Ora, dal momento che gli identificatori sono generate dinamicamente, e quindi utilizzare di 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, non proprio. Infatti, partial potrebbe essere usato anche qui.

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

sarebbe diventato

primo argomento
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 metodo istanza ha sempre bisogno di essere l'istanza. La convenzione è denominare tale parametro self, ma è possibile utilizzare qualsiasi altro nome.Non lo consiglierei, poiché renderà il tuo codice più difficile da leggere e capire.

  1. What point is that lambda evaluated?

lambda crea funzione anonima, che come ogni funzione viene valutata su convocazione.

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

Non lo è. È self.check(), dove self è un'istanza della classe Tests.

+2

In realtà, 'operator.methodcaller (" check ", 1, 2)' è preferibile su 'partial' qui perché usa la digitazione anatra e non richiede un'istanza di' Tests'. Questo commento vale anche per la tua risposta all'altra domanda. –

+0

@NiklasB .: ok, ma la domanda era se 'partial' è qualcosa di opposto di' lambda', non se è il modo migliore – vartec

+0

Certo, forse avrei dovuto aggiungerlo meglio alla tua altra risposta. –