> @wrap_notify('remove', 'clear', 'append', 'insert', 'sort'): > class ObservableList(ObservableCollection, list): > pass > > I just can't find out the right syntax. > > All right, this is working.
from contextlib import contextmanager class ObservableCollection: @contextmanager def notify(cls): print("notify before change") yield print("notify after change") def wrap_notify(cls, basecls, method_names): for method_name in method_names: orig = getattr(basecls, method_name) def modified(self, *args, **kwargs): with self.notify(): return orig(self,*args,**kwargs) setattr(cls, method_name, modified) return cls class ObservableDict(ObservableCollection, dict): __slots__ = [] wrap_notify(ObservableDict, dict, ['update', '__delitem__', '__setitem__']) # many others... class ObservableList(ObservableCollection, list): __slots__ = [] wrap_notify(ObservableList, list, ['append', 'pop']) # many others... d = ObservableDict() d[1] = 2 print(d) But I'm still not 100% satisfied. wrap_notify is not a class decorator. Is there a way to make a class decorator that gets the class as its first parameter? It would be very nice: def wrap_notify(cls, method_names): class Wrapped(cls): pass for method_name in method_names: orig = getattr(Wrapped, method_name) def modified(self, *args, **kwargs): with self.notify(): return orig(self,*args,**kwargs) setattr(cls, method_name, modified) return Wrapped @wrap_notify(['update', '__delitem__', '__setitem__']) # many others... class ObservableDict(ObservableCollection, dict): __slots__ = [] @wrap_notify(['append', 'pop']) # many others... class ObservableList(ObservableCollection, list): __slots__ = [] And finally: is this Pythonic, or a horrible mess? Would it be better to duplicate the body of each method one by one? -- https://mail.python.org/mailman/listinfo/python-list