Re: [Python] Gestire eventi con callback

2015-03-09 Per discussione Alessandro Re
Alla fine *credo* di aver trovato una soluzione *quasi* soddisfacente.
Alcune scelte fanno cagare, ma per ora può andare.
Condivido, per un ovvio atto di giustizia cosmica.

2015-03-08 15:12 GMT+00:00 Marco Giusti :
> Il patter che più gli assomigli è l'Observer ma non sono sicuro che la
> terminologia sia quella giusta in questo caso. Comunque
> l'implementazione è all'incirca quella:
>
> ...
> self._events = {}
>
> def register(self, event, callback):
> self._events.setdefault(event, []).append(callback)
>
> def unregister(self, event, callback):
> callbacks = self._events[event]
> callbacks.pop(callbacks.index(callback))
>
> def on_event(self, event, *args):
> for callback in self._events[event]:
> callback(*args)

Questa implementazione va sicuramente bene per un meccanismo basilare,
e le ho usate. Bene, Grazie Marco :)

Però poi mi sono accorto che volevo qualcosa di più elaborato ed
automatico. Fondamentalmente, quello che ho fatto è mettere in
register() un sistema che, quando passo un oggetto, io registro in
quell'oggetto una callback che - all'atto di distruzione - rimuove la
callback originale. E questa callback che rimuove, viene schedulata
per essere eseguita una volta sola, e poi rimossa automaticamente.

Inoltre, per permettere di usare funzioni che incapsulassero i vari
metodi, ho fatto in modo che register() prenda anche come parametro un
oggetto da passare come primo argomento alla callback.

In sostanza, il codice è questo:

class Base:
def register(self, event, target_obj, callback):
if target_obj is None:
def _call_once(*args, **kwargs):
callback(*args, **kwargs)
self.unregister(event, None, _call_once)
self._callbacks.setdefault(event, []).append((None, _call_once))
else:
self._callbacks.setdefault(event, []).append((target_obj, callback))
def _remove_cb(unused, obj):
self.unregister(event, target_obj, callback)
target_obj.register('on_remove', None, _remove_cb)

def unregister(self, event, target_obj, callback):
self._callbacks[event].remove((target_obj, callback))

def _run_callbacks(self, event, *args, **kwargs):
for obj, cb in list(it for it in self._callbacks.get(event, [])):
cb(obj, *args, **kwargs)

def remove(self):
self._run_callbacks('on_remove', self)

Scelte di design:
 - passare l'oggetto (target_obj) esplicitamente in register()
 - schedulare una callback come "fire once" se target_obj è None.
Avrei potuto usare un protocollo diverso, ma questo mi è sembrato un
buon compromesso per ragioni che non sto a spiegare
 - lasciare che quando la callback è "fire once", le venga passato
None come primo parametro. Solo per consistenza col resto
 - ogni callback registrata avrà una callback automatica di
de-registrazione quando l'oggetto viene distrutto. Probabilmente è uno
spreco di memoria e ci sono soluzioni migliori (tipo un metodo solo
che rimuove tutte le callback associate all'oggetto, la lo lascio come
esercizio al lettore :D).

Punti interessanti:
 - in _run_callback costruisco una nuova lista perché può essere che
cb chiami unregister() durante l'esecuzione. Probabilmente non è la
soluzione migliore, comunque.
 - remove fa altre cose, ma è anche un esempio di come usare _run_callbacks.

Non ho fatto dei gran test, ma per ora funzionicchia.
Commenti ben accetti - ma considerate che è piuttosto legato al mio
stile e alle necessità di questa applicazione, non l'ho pensato perché
fosse generico.
Ciauz
~Ale
___
Python mailing list
Python@lists.python.it
http://lists.python.it/mailman/listinfo/python


Re: [Python] Smettete di scrivere shell script (era: Re: Lanciare script da altro script)

2015-03-09 Per discussione enrico franchi
2015-03-06 17:08 GMT+00:00 Enrico Bianchi :

> On Wednesday, March 04, 2015 07:39:20 AM enrico franchi wrote:
>
> > Se vuoi usare Python per fare automazione di cose, ti troverai spesso ad
> > usare subprocess. Mica per altro... semplicemente molti task specifici
> non
> > hanno un equivalente di libreria (e scriverlo costa troppo),
>
> E` per questi casi che dico che subprocess dev'essere usato solo se
> strettamente necessario
>


Senti, davvero... io non riesco a seguire i tuoi flussi di pensiero. Questo
thread, e' nato da OP che parlava appunto di come fare a scriptare il fatto
che doveva lanciare quattro programmi. Da cui il mio suggerimento di usare
bash. E il fatto che, per una cosa cosi' semplice, trovo farlo in Python
con subprocess piu' scomodo che farlo in bash.

Questo giusto per dare contesto. Per il resto, nessuno dice che devi usare
subprocess a sproposito o simili. Io mi limito a dire che usare subprocess
"al posto di bash" non e' comodo. O meglio, io non lo trovo comodo.

Tutto il resto sono discussioni su casi specifici che non hanno senso senza
vedere i casi specifici stessi.


>
> > Tra l'altro, sei d'accordo con me che si, bash puo' avere senso. Giuro
> che
> > non capisco quale sia il punto.
>
> Il punto e` che scelgo di usare Python lo faccio perche` posso usare la sua
> libreria, e non comandi shell, che mi viene comoda in una serie smodata di
> contesti
>

Dai, rileggiti il thread, davvero. Io credo che siamo in quasi completo
accordo su tutta la faccenda. Non e' che con tutti questi interventi
pensavi di rispondere a qualcun altro?

Cioe' la mia tesi e' che per chiamare quattro comandi shell in croce sia da
valutare il fatto che usare bash puo' essere piu' comodo farlo in bash che
usando subprocess. Non stiamo parlando di complessi controlli di flusso, di
librerie, di niente.



-- 
.
..: -enrico-
___
Python mailing list
Python@lists.python.it
http://lists.python.it/mailman/listinfo/python


Re: [Python] Gestire eventi con callback

2015-03-09 Per discussione Manlio Perillo
2015-03-08 21:31 GMT+01:00 Alessandro Re :

> 2015-03-08 20:24 GMT+00:00 Manlio Perillo :
> > Intendevo dire cosa usi per chiamare il codice C++.
>
> Ah, forse ho capito: intendi dire se uso qualche modulo python per
> usare C++, tipo ctypes?
> Non uso niente di simile: sto usando una libreria che ha dei binding
> in python, quindi includo il modulo, eredito una classe e costruisco
> su quella.
>
>
L'unica soluzione "semplice" che  mi venga in mente, è quella di costruire
dei proxy alle instanze delle classi C++ che usi,
così come il modulo weakref lo fa per oggetti Python.

Che io sappia in C++ l'unico modo per creare un proxy per una classe CT è
quello di creare una sotto classe e ridefinire i costruttori e il
distruttore.

class PT public CT {
   // nel costruttore accetti una callback
   // nel distruttore chiami la callback
}

Quindi quando il codice originale richiede un CT, tu instanzi e passi un PT.
Temo però che per casi non banali sia un bagno di sangue farlo a mano.
Però se la cosa è fattibile, sicuramente qualcuno l'ha già implementata


Ciao  Manlio
___
Python mailing list
Python@lists.python.it
http://lists.python.it/mailman/listinfo/python


Re: [Python] Lanciare script da altro script

2015-03-09 Per discussione enrico franchi
2015-03-06 17:23 GMT+00:00 Enrico Bianchi :

> On Tuesday, March 03, 2015 03:28:15 PM enrico franchi wrote:
>
> (reinvio per scazzi con client email)
>
>  3. e' qualcosa che fa "solo" I/O bound; per il CPU bound devi sempre
>> andare
>> vecchia maniera. multiprocessing o incrociare le dita con i thread.
>>
>
> https://docs.python.org/3/library/concurrent.futures.html ?
>
>
Si, leggila la pagina che linki pero'... :)
Quella e' una API costruita su... multiprocessing oppure threading. In
altre parole, siamo punto a capo.


-- 
.
..: -enrico-
___
Python mailing list
Python@lists.python.it
http://lists.python.it/mailman/listinfo/python