Nagy László Zsolt wrote: > >> Using the built-in list or dict is problematic because sometimes the >> subclass methods are ignored when the internal data is accessed (sorry, I >> don't have an example). > Are you suggesting that I should use UserList and UserDict instead of > list and dict? What is the truth? Was UserDict left in collections for > backward compatibility, or not? > >> >> How detailed is your change notification? If it's always the same method >> invocation you may be able to create wrappers like >> >> def whatever(self, *args): >> try: >> return super().whatever(*args) >> finally: >> self.changed() >> >> automatically, and the base class, be it list or UserList or whatever >> becomes a detail that can be chosen on a case by case basis. > My actual solution looks like this: > > > class ObservableCollection(Observable): > @contextmanager > def notify(self): > self.notify_observers(EVT_BEFORE_COLLECTION_CHANGED) > yield > self.notify_observers(EVT_AFTER_COLLECTION_CHANGED) > > class ObservableList(ObservableCollection, list): > __slots__ = [] > > def remove(self, value): > with self.notify(): super().remove(value) > > def clear(self): > with self.notify(): super().clear() > > def pop(self): > with self.notify(): return super().pop() > > It seems to be working with the built in list, dict and set types. > > There must be a better way! Something like this: > > @wrap_notify('remove', 'clear', 'append', 'insert', 'sort'): > class ObservableList(ObservableCollection, list): > pass > > I just can't find out the right syntax.
[Looks like you made progress while I struggled to come up with the following. I'll post it anyway.] $ cat observable_list.py from contextlib import contextmanager EVT_BEFORE_COLLECTION_CHANGED = "EVT_BEFORE_COLLECTION_CHANGED" EVT_AFTER_COLLECTION_CHANGED = "EVT_AFTER_COLLECTION_CHANGED" class Observable: def notify_observers(self, message): print(message) def wrap_notify(*names): def wrap_methods(cls): for name in names: method = wrap_method(getattr(cls, name), name) setattr(cls, name, method) return cls return wrap_methods def wrap_method(method, name): def wrapped_method(self, *args, **kw): with self.notify(): return method(self, *args, **kw) return wrapped_method class ObservableCollection(Observable): @contextmanager def notify(self): self.notify_observers(EVT_BEFORE_COLLECTION_CHANGED) yield self.notify_observers(EVT_AFTER_COLLECTION_CHANGED) @wrap_notify('remove', 'clear', 'append', 'insert', 'sort') class ObservableList(ObservableCollection, list): pass items = ObservableList() items.append(42) items.append(41) items.sort() print(items) $ python3 observable_list.py EVT_BEFORE_COLLECTION_CHANGED EVT_AFTER_COLLECTION_CHANGED EVT_BEFORE_COLLECTION_CHANGED EVT_AFTER_COLLECTION_CHANGED EVT_BEFORE_COLLECTION_CHANGED EVT_AFTER_COLLECTION_CHANGED [41, 42] -- https://mail.python.org/mailman/listinfo/python-list