[Python] Gestire eventi con callback

Marco Giusti marco.giusti a posteo.de
Dom 8 Mar 2015 08:54:10 CET


On Sun, Mar 08 2015, Alessandro Re wrote:
> Ciao lista,
> 
> ho un problema con delle callback, e sapendo che ci sono sistemi in
> cui si usano molto (e.g. in ambito web), magari qualcuno di voi mi può
> illuminare (scusate, il post è un po' lungo).
> 
> Ho degli oggetti python che fanno riferimento a degli oggetti C++,
> quindi a volte mi trovo nella situazione in cui un oggetto C++ viene
> distrutto e, se l'oggetto python esiste ancora e cerca di far
> riferimento ad esso, provoca - giustamente - errore.
> 
> Ora, dal lato python ho delle callback per gestire degli eventi che
> arrivano da C++: quando un certo evento accade, una funzione python
> viene chiamata.
> 
> Quindi immaginate di avere un oggetto A che emette un evento E, ed un
> oggetto B che ha una callback C associata ad E: quando E accade, C
> viene chiamata.
> 
> Il problema è questo: può capitare che un metodo remove() di B venga
> chiamato, provocando la rimozione dell'oggetto C++ sottostante; ma non
> dell'oggetto python B, perché la sua callback è ancora registrata. Se
> per caso in C venisse usato l'oggetto C++, si alzerebbe una bella
> eccezione.
> 
> Ora, la mia domanda è: come posso costruire in python un sistema in
> cui io posso attaccare due oggetti, ma quando un certo metodo (e.g.
> remove) viene chiamato, allora tutte le callback registrate vengono
> rimosse senza troppo sforzo e tutti gli oggetti garbage-collected come
> ci si aspetterebbe?

C == oggetto in c++ con riferimento all'oggetto python
P == oggetto python

Come registri le callback? Gli oggetti in python sono referenziati da
del codice python o solo dalle callback di c++? In questo caso basta
che, nel momento in cui distruggi l'oggetto C, deregistri le callback
e riduci il numero di referenze del metodo. Se i metodi di un oggetto
P sono usati come callback di un solo oggetto C, al momento che
tu rilasci tutte le callback non esistono più riferimenti all'oggetto
python.

Se al momento della distruzione dell'oggetto C vuoi chiamare un'ultima
callback, chiama la callback e poi rimuovi il riferimento alla stessa.

> Conoscete dei pattern, degli idiomi, o qualunque cosa per poter
> implementare questo?
> 
> Ho poi una seconda domanda, che fortunatamente richiede meno parole :)
> 
> A volte, viene dato un certo comando, ad esempio "send_data(123)";
> questa operazione viene fatta in modo asincrono, cioé il controllo
> torna subito all'utente e una callback verrà chiamata quando
> l'operazione è stata completata.
> 
> Come posso rendere sincrona (bloccante) l'invocazione di un comando?
> 
> Un modo tanto semplice quanto barbaro è il seguente:
> 
> free_to_go = False
> def on_packet_sent():
>     global free_to_go
>     free_to_go = True
> 
> def send_data_blocking(data):
>     global free_to_go
>     free_to_go = False
>     send_data(data, callback=on_packet_sent)
>     while not free_to_go:
>          pass
> 
> send_data_blocking(123)

La prima idea che mi viene in mente è di infilare la callback dentro la
send e di chiamare sleep per salvare qualche ciclo macchina:

    def send_data_blocking(data):
        def go():
            free_to_go = True
        free_to_go = False
        send_data(data, callback=go)
        while not free_to_go:
             sleep(0.1)


abbastanza naïve ma non dai tante informazioni a proposito e quindi per
esempio nessuna gestione degli errori. send_data a parte il codice è
thread-safe.

ciao
m.


Maggiori informazioni sulla lista Python