UserList - which methods needs to be overriden?
Peter Otten
__peter__ at web.de
Thu Jun 9 10:13:31 EDT 2016
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]
More information about the Python-list
mailing list