[Python] throw ad un generatore: perché non riparte dall'inizio?

Marco Giusti marco.giusti a posteo.de
Gio 28 Lug 2022 09:37:21 CEST


On 28.07.2022 08:56, Marco De Paoli wrote:
> ciao lista!
> ho una domanda sui generatori
> Mi farebbe molto comodo usare la possibilità di mandare una eccezione
> ad un generatore: ma mi trovo con un comportamento imprevisto
> Qui sotto trovate un frammento di codice che riproduce una sorta di
> "caso minimo" di quella che mi sembra una anomalia
> Se avete voglia di provarlo credo che il codice sia più o meno
> autoesplicativo.
> Quello che mi lascia perplesso è: perché il generatore, dopo aver
> gestito l'eccezione, non riparte dal restituire il valore "A"?

Ciao Marco,
   ho aggiunto qualche print in piu' e come vedi dopo aver lanciato 
l'eccezione,
due print sono eseguite prima di produrre il valore "A". Quello che 
sospetto
sia il flusso di esecuzione e' il seguente.

1. un'eccezione e' lanciata all'interno del generatore, il flusso 
ritorna
     all'inizio del ciclo. La prima print e' eseguita cosi' come la prima 
yield;
2. fuori dal generatore il codice non consuma il valore generato usando 
una
     chiamata a "next", ma invece manda un altro valore (None) usando una 
send.
     Il generatore avanza, genera il valore "B" e questo e' il valore di 
ritorno
     della send e quello che viene stampato a schermo.

Questa e' una presentazione [1] che lessi tempo addietro. La trovai 
illuminante
all'epoca e trovo che sia sempre attuale. Questa e' la slide 31:

     * Despite some similarities, Generators and
       coroutines are basically two different concepts
     * Generators produce values
     * Coroutines tend to consume values
     * It is easy to get sidetracked because methods
       meant for coroutines are sometimes described as
       a way to tweak generators that are in the process
       of producing an iteration pattern (i.e., resetting its
       value). This is mostly bogus.

La slide 32 e' similare al tuo esempio, e come vedi un valore e' perso
esattamente come nel tuo caso.

Spero di essere stato d'aiuto.

Marco

[1] http://www.dabeaz.com/coroutines/Coroutines.pdf



# test_gen.py

     def gen():
         while True:
             print("begin of the loop")
             try:
                 print("before the first yield")
                 yield "A"
                 print("after the first yield")
                 yield "B"
                 yield "C"
             except Exception as ex:
                 print("catched exception", ex)


     g = gen()
     you_can_specify_any_number_of_steps = 3
     for idx in range(you_can_specify_any_number_of_steps):
         print("result:", g.send(None))
     print("throw...")
     g.throw(Exception("BOOM"))
     print("result:", g.send(None), "*** I was expecting A and I get B, 
why?!? ***")
     print("result:", g.send(None))

# output

     $ python3.10 test_gen.py
     begin of the loop
     before the first yield
     result: A
     after the first yield
     result: B
     result: C
     throw...
     catched exception BOOM
     begin of the loop
     before the first yield
     after the first yield
     result: B *** I was expecting A and I get B, why?!? ***
     result: C



Maggiori informazioni sulla lista Python