2011-01-19 3 views
5

Come si accede a una variabile locale definita all'interno di un generatore Python dall'esterno del generatore?Accesso alle variabili locali all'interno di un generatore Python

Ho un caso in cui il mio generatore manipola uno stato locale, e per le unittests voglio controllare questo stato per assicurarmi che contenga i valori corretti.

Non riesco a memorizzare lo stato su una variabile di istanza (ad esempio self.state = blah), perché potrei creare più generatori dalla stessa istanza di classe, il che significa che i generatori potrebbero sovrascrivere lo stato dell'altro. Inoltre, non è possibile restituire lo stato nell'espressione yield, poiché il nome dello stato può variare o variare a causa delle singole istanze del generatore.

ad es. Voglio fare qualcosa di simile (anche se questo codice non funziona)

from random import random 

class MyIter(object): 
    def __iter__(self): 
     context = {} 
     for i in xrange(10): 
      context[random()] = random() 
      yield i 

obj = MyIter() 
i1 = iter(obj) 
i2 = iter(obj) 
while 1: 
    try: 
     i1.next() 
     i2.next() 
     print i1.context 
     print i2.context 
    except StopIteration: 
     break 

Esiste un modo per accedere alle variabili locali ispezionando stack di esecuzione di Python?

risposta

3

Mi dispiace di rispondere alla mia domanda , ma dopo aver scavato nell'interfaccia del generatore, ho trovato il percorso esatto di cui ho bisogno per accedere alle variabili locali del generatore:

from random import random 

class MyIter(object): 
    def __iter__(self): 
     context = {} 
     for i in xrange(10): 
      context[random()] = random() 
      yield i 

obj = MyIter() 
i1 = iter(obj) 
i2 = iter(obj) 
while 1: 
    try: 
     i1.next() 
     i2.next() 
     print i1.gi_frame.f_locals['context'] 
     print i2.gi_frame.f_locals['context'] 
    except StopIteration: 
     break 
+2

Ouch! Non farlo, è un dettaglio di implementazione di CPython. Potrebbe cambiare con le versioni più recenti ed essere diverso per diverse implementazioni di Python (come Jython o IronPython). E non è nemmeno leggibile ... –

+0

È molto improbabile che utilizzi mai Jython o IronPython, ed entrambi hanno molte incompatibilità con la funzionalità CPython "standard", ma sono ben presi. – Cerin

+0

+1 Anche se non farei mai affidamento su alcun codice che deve accedere allo stato interno di un generatore durante il normale funzionamento, * è * molto utile per essere in grado di ottenere questi dati durante il debug dato che ci sono state alcune volte quando Python mi ha lasciato chiedendo: "A cosa stavi pensando?" (Inoltre, non mi sentirei mai male nel rispondere alla mia domanda, a meno che la risposta fosse sbagliata;)) – Augusta

0

Si dovrebbe trattare il generatore come una scatola nera. I test unitari non dovrebbero preoccuparsi del suo stato interno, perché questo è solo un dettaglio di implementazione; dovrebbero preoccuparsi solo del comportamento specificato.

0

Se si vuole veramente fare questo, separare la classe iteratore dalla classe contenitore:

from random import random 

class MyContainer(object): 
    def __iter__(self): 
     return MyIter(self) 

class MyIter(object): 
    def __init__(self, container): 
     self.container = container 
     self.context = {} 
     self.it = iter(xrange(10)) 
    def next(self): 
     self.context[random()] = random() 
     return next(self.it) 
    def __iter__(self): 
     return self 

obj = MyContainer() 
# ... 

non ritengo questo molto utile anche se ...

+0

Grazie. Questo è un approccio molto pulito e OO, anche se aggiunge un po 'di complessità in più. – Cerin