On Mon, Mar 09 2015, Alessandro Re wrote: > 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 <marco.giu...@posteo.de>: > > 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)
Credo che puoi fare meglio che questo. Vuoi che ogni callback venga invocata una sola volta? def _run_callbacks(self, event, *args, **kwds): assert event in self._callbacks callbacks = self._callbacks[event] while callbacks: obj, callback = callbacks.pop() # .pop(0); deque? try: callback(obj, *args, **kwds) except: logger.exception("useful error message") Inoltre passare un oggetto come primo argomento mi suona tanto da antipattern. Non è sbagliato, ma chiediti che oggetto sia. Mi suona da antipattern per come i metodi e le funzioni in python funzionano. Confronta le due dichiarazioni e come vengono chiamate: def metodo(self, *args, **kwds): ... def funzione(*args, **kwds): ... m = Object().metodo m(*args, **kwds) funzione(*args, **kwds) come vedi le due chiamate sono identiche ma in più il metodo riceve un oggetto (su cui opera il metodo). Il tuo codice sembra reinventare una ruota che ormai è ben collaudata. Comunque l'ultima parola l'hai tu perché il codice che hai postato non ci aiuta molto a capire esattamente il funzionamento di questi fantomatici oggetti C++. Ciao m. PS. Le tue linee di codice sono identiche: for obj, cb in list(it for it in self._callbacks.get(event, [])): for obj, cb in self._callbacks.get(event, [])[:]: > 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. _______________________________________________ Python mailing list Python@lists.python.it http://lists.python.it/mailman/listinfo/python