2013-09-05 3 views
6

Ho un coroutine (generatori aumentate) in pitone con qualche codice da eseguire dopo la fine dei dati:Evitare di "eccezione ignorata" in pitone migliorato generatore

def mycoroutine(): 
    try: 
    while True: 
     data = (yield) 
     print data 
    finally: 
    raise ValueError 
    print "END" 

co = mycoroutine() 
co.next() 

for i in (1,2,3): 
    co.send(i) 

L'eccezione ValueError non viene generato, ma l'interprete stampa semplicemente:

Exception ValueError: ValueError() in <generator object mycoroutine at 0x2b59dfa23d20> ignored 

C'è un modo per rilevare l'eccezione nel chiamante?

risposta

9

L'eccezione è sollevata. Il blocco finally viene eseguito quando il generatore viene chiuso. La chiusura di un generatore viene effettuata aumentando un GeneratorExit exception nel contesto del generatore.

L'eccezione in ignorato perché il generatore non è chiuso fino a quando non viene eliminato (automaticamente in questo caso, quando Python termina); il conduttore generatore __del__ chiude il generatore, che attiva il blocco finally::

>>> def mycoroutine(): 
... try: 
...  while True: 
...  data = (yield) 
...  print data 
... finally: 
...  raise ValueError 
...  print "END" 
... 
>>> co = mycoroutine() 
>>> co.next() 
>>> co.close() 
Traceback (most recent call last): 
    File "<stdin>", line 1, in <module> 
    File "<stdin>", line 7, in mycoroutine 
ValueError 
>>> co = mycoroutine() 
>>> co.next() 
>>> del co 
Exception ValueError: ValueError() in <generator object mycoroutine at 0x1046a9fa0> ignored 

eccezioni sollevate durante pulitura vengono sempre ignorati; vedere la object.__del__() documentation:

Attenzione: A causa delle circostanze precarie in cui vengono chiamati i __del__() metodi, eccezioni che si verificano durante la loro esecuzione sono ignorati, e un avvertimento viene stampato sys.stderr invece.

La soluzione è quella di non avere eccezioni essere sollevate quando un generatore è ripulito, o intercettare l'eccezione chiudendo il generatore in modo esplicito:

>>> co = mycoroutine() 
>>> co.next() 
>>> try: 
...  co.close() 
... except ValueError: 
...  pass 
... 
>>> del co 
>>> # No exception was raised 
... 

Si potrebbe anche intercettare l'eccezione GeneratorExit ed eseguire un po di pulizia a quel punto:

def mycoroutine(): 
    try: 
    while True: 
     data = (yield) 
     print data 
    except GeneratorExit: 
    print "Generator exiting!" 

ma si noti che qualsiasi eccezione diverso sarà sempre essere propagati StopIteration o GeneratorExit; vedere la generator.close() documentation:

Se la funzione di generatore solleva poi StopIteration (chiudendo normalmente, oa causa di già essendo chiusa) o GeneratorExit (da non prendere l'eccezione), vicino torna al suo chiamante. Se il generatore produce un valore, viene generato un valore RuntimeError. Se il generatore genera altre eccezioni, viene propagato al chiamante.

+0

La stessa cosa accade se sostituisco 'finally'" con 'tranne GeneratorExit:' Suppongo che sia lo stesso ... – Zac

+0

@Zac:. Naturalmente lo fa, è ancora * * rilancio di un 'ValueError' , non si? Cattura 'GeneratorExit' e sollevando un'altra eccezione non impedisce l'propagazione dell'eccezione. –