2015-10-29 14 views
11

In Python 3.5.0 di questo codice:Perché ottengo questo NameError in un generatore all'interno di una definizione di classe Python?

a = (1,2) 
class Foo(object): 
    b = (3,4) 
    c = tuple((i,j) for j in b for i in a) 
    d = tuple((i,j) for i in a for j in b) 

produce:

Traceback (most recent call last): 
    File "genexprtest.py", line 2, in <module> 
    class Foo(object): 
    File "genexprtest.py", line 5, in Foo 
    d = tuple((i,j) for i in a for j in b) 
    File "genexprtest.py", line 5, in <genexpr> 
    d = tuple((i,j) for i in a for j in b) 
NameError: name 'b' is not defined 

Perché ottengo questo errore? E perché non ottengo questo errore sulla riga precedente?

+1

Poiché le espressioni del generatore e le definizioni di classe sono entrambi il loro ambito di applicazione – jonrsharpe

+0

Ma se sono entrambi nel loro ambito, perché diavolo fa l'accesso a b nella riga precedente (c = ...) ha successo? – Wangnick

+0

Nel primo esempio, 'b' viene ripetuto nell'outer più esterno' for', che viene valutato immediatamente - vedi ad es. https://www.python.org/dev/peps/pep-0289/#early-binding-versus-late-binding per la logica. Allo stesso modo, se si cambia l'esempio nei documenti in 'b = list (i per i in range (a))' funziona correttamente, e 'd = tuple ((i, j) per i, j in itertools.product (b, a)) 'funzionerà in entrambi i modi. – jonrsharpe

risposta

0

Ho passato anni a sperimentare e ho una teoria sul perché si sta verificando questo errore. Non sono sicuro, ma questo spiega perché funziona per c e non per d. Spero che questo ti aiuta, commento, se siete d'accordo :)

def Tuple(this): 
    print(a) # this always works 
    try: 
     print(b) # this always gives an error 
    except NameError: 
     print("...b is not defined") 
    try: 
     return tuple(this) # this only gives an error for d and e 
    except NameError: 
     print("...couldn't make it a tuple") 


a = (1,2)  
class Foo(object): 
    b = (3,4) 
    c = Tuple((i,j) for j in b for i in a) 
    d = Tuple((i,j) for i in a for j in b) 
    e = Tuple((i,j,k) for i in a for j in b for k in (5, 6)) 
    f = Tuple((i,j,k) for j in b for i in (5, 6) for k in a) 

    print("\nc:", c,"\nd:", d,"\ne:", e,"\nf:", f) 

Che cosa è successo: ogni volta che ho chiamato la funzione Tuple(), b non è stato definito, ma a era sempre definito. Questo spiega il motivo per cui si ottiene un errore per d e e ma non spiega perché c e f lavoro anche se è b 'non definito'

La mia teoria: Il primo for ciclo viene calcolato prima che il tutto è convertito in una tupla. Per esempio, se si è tentato di fare questo: Tuple((a, b, c) for a in loop1, for b in loop2 for c in loop3), nella classe Foo sarebbe calcolare for a in loop1 prima, allora sarebbe passare al foo e calcolare i cicli 2 e 3.

In sintesi:

  1. fa primo ciclo
  2. sposta funzione tupla
  3. fa passanti residua
  4. l'errore si verifica se una variabile nel 2 ° o 3 ° ciclo è in classe Foo
0

A mio parere, l'errore si verifica perché b è definito come una variabile di classe. Per usarlo correttamente, è necessario trattarlo come tale (self.b). Inoltre, si dovrebbe usare costruttore:

a = (1, 2) 

class Foo(object): 
    def __init__(self): 
     self.b = (3, 4) 
     self.c = tuple((i, j) for j in self.b for i in a) 
     self.d = tuple((i, j) for i in a for j in self.b) 

Si tratta di un codice più chiaro. E si comporta correttamente. Spero che sia d'aiuto.

EDIT: se non si desidera utilizzare __init__, c'è anche la possibilità di ottenere c e d utilizzando metodi:

a = (1, 2) 

class Foo(object): 
    b = (3, 4) 

    def get_c(self): 
     return tuple((i, j) for j in self.b for i in a) 

    def get_d(self): 
     return tuple((i, j) for i in a for j in self.b) 

questo funziona anche perfettamente bene. Si può provare entrambe le implementazioni in questo modo:

inst = Foo() 
# 1st one 
print(inst.c) 
print(inst.d) 
# 2nd one 
print(inst.get_c()) 
print(inst.get_d()) 
0

Questo perché l'espressione for i in a ha un locale portata variabile, e l'espressione for j in b si trova all'interno del campo di applicazione, in tal modo, non b viene trovato.
In realtà, se si scrive c = tuple((i, j) for i in a for j in b, verrà generata la stessa eccezione.

La soluzione viene inserita nella definizione della classe b e la fa riferimento per self.b.

0

La soluzione al vostro caso specifico è quello di utilizzare itertools:

d = tuple(itertools.product(a, b)) 

Il spiegazione per il comportamento apparentemente imprevisto è duplice: attributi

  1. classe Bare come b sono accessibile solo nello scope della classe radice . Vedi pep 227:

    I nomi nella portata della classe non sono accessibili. I nomi sono risolti nello scope della funzione di chiusura più interna. Se una definizione di classe si verifica in una catena di ambiti nidificati, il processo di risoluzione ignora le definizioni di classe.

  2. cicli annidati in generatori non funzionano come ci si potrebbe aspettare. Il primo ciclo è in realtà il più esterno e il secondo il più interno. Da python docs:

    Le successive clausole non possono essere valutate immediatamente poiché possono dipendere dal ciclo precedente. Ad esempio: (x * y per x nell'intervallo (10) per y nella barra (x)).

Il secondo punto può essere illustrato con aggiunta di interruzioni di linea.

d = tuple((i,j) 
    for i in a 
     for j in b) 

Ciò significa che b è effettivamente fatto riferimento dal ciclo interno (portata nested) e quindi una NameError è gettato. Nel primo generatore, tuttavia, il riferimento è in quello esterno che funziona bene.

Problemi correlati