Groups | Search | Server Info | Keyboard shortcuts | Login | Register [http] [https] [nntp] [nntps]


Groups > comp.lang.python > #109750

Re: UserList - which methods needs to be overriden?

Path csiph.com!fu-berlin.de!uni-berlin.de!not-for-mail
From Peter Otten <__peter__@web.de>
Newsgroups comp.lang.python
Subject Re: UserList - which methods needs to be overriden?
Date Thu, 09 Jun 2016 16:13:31 +0200
Organization None
Lines 111
Message-ID <mailman.117.1465481634.2306.python-list@python.org> (permalink)
References <b245d88e-9b90-bb90-bd2a-13ee82f7a5c8@shopzeus.com> <njbdhk$pv2$1@ger.gmane.org> <917ca9c4-aef1-6681-ec70-62de8916117d@shopzeus.com> <njbp5d$mn9$1@ger.gmane.org> <7cdb398f-b3f2-9a3c-b444-2229f8496e2f@shopzeus.com> <njbtif$i2$1@ger.gmane.org>
Mime-Version 1.0
Content-Type text/plain; charset="UTF-8"
Content-Transfer-Encoding 8Bit
X-Trace news.uni-berlin.de 0drH/gKdFdHDA8w+2v/smA/0r196NjPVhnwUG8/Wdj1w==
Return-Path <python-python-list@m.gmane.org>
X-Original-To python-list@python.org
Delivered-To python-list@mail.python.org
X-Spam-Status OK 0.000
X-Spam-Evidence '*H*': 1.00; '*S*': 0.00; 'finally:': 0.05; 'ignored': 0.05; 'python3': 0.05; '*args,': 0.07; '*args):': 0.09; 'collections': 0.09; 'dict': 0.09; 'invocation': 0.09; 'name)': 0.09; 'name):': 0.09; 'received:80.91': 0.09; 'received:80.91.229': 0.09; 'received:gmane.org': 0.09; 'received:list': 0.09; 'subclass': 0.09; 'subject:which': 0.09; 'def': 0.13; '(sorry,': 0.16; '__slots__': 0.16; 'clear(self):': 0.16; 'cls': 0.16; 'contextlib': 0.16; 'example).': 0.16; 'list):': 0.16; 'message):': 0.16; 'name),': 0.16; 'received:80.91.229.3': 0.16; 'received:dip0.t-ipconnect.de': 0.16; 'received:io': 0.16; 'received:plane.gmane.org': 0.16; 'received:psf.io': 0.16; 'received:t-ipconnect.de': 0.16; 'remove(self,': 0.16; 'skip:@ 20': 0.16; 'userdict': 0.16; 'wrappers': 0.16; 'wrote:': 0.16; 'try:': 0.18; 'skip:" 30': 0.20; 'class,': 0.22; 'pass': 0.22; 'seems': 0.23; 'this:': 0.23; 'import': 0.24; 'header:User-Agent:1': 0.26; 'header:X-Complaints- To:1': 0.26; 'yield': 0.27; 'actual': 0.28; 'looks': 0.29; 'cat': 0.29; 'types.': 0.29; 'becomes': 0.30; 'post': 0.31; "can't": 0.32; 'class': 0.33; "i'll": 0.33; 'list': 0.34; 'basis.': 0.35; 'something': 0.35; 'sometimes': 0.35; 'list,': 0.36; 'should': 0.36; 'instead': 0.36; 'there': 0.36; 'to:addr:python-list': 0.36; 'subject:?': 0.36; 'subject:: ': 0.37; 'method': 0.37; 'received:org': 0.37; 'skip:s 50': 0.37; 'detail': 0.38; 'progress': 0.38; 'data': 0.39; 'whatever': 0.39; 'skip:e 20': 0.39; 'to:addr:python.org': 0.40; 'received:de': 0.40; 'your': 0.60; 'skip:n 10': 0.62; 'accessed': 0.66; '**kw)': 0.84; '**kw):': 0.84; 'nagy': 0.84; 'subject:needs': 0.93
X-Injected-Via-Gmane http://gmane.org/
X-Gmane-NNTP-Posting-Host p57bd9048.dip0.t-ipconnect.de
User-Agent KNode/4.13.3
X-BeenThere python-list@python.org
X-Mailman-Version 2.1.22
Precedence list
List-Id General discussion list for the Python programming language <python-list.python.org>
List-Unsubscribe <https://mail.python.org/mailman/options/python-list>, <mailto:python-list-request@python.org?subject=unsubscribe>
List-Archive <http://mail.python.org/pipermail/python-list/>
List-Post <mailto:python-list@python.org>
List-Help <mailto:python-list-request@python.org?subject=help>
List-Subscribe <https://mail.python.org/mailman/listinfo/python-list>, <mailto:python-list-request@python.org?subject=subscribe>
X-Mailman-Original-Message-ID <njbtif$i2$1@ger.gmane.org>
X-Mailman-Original-References <b245d88e-9b90-bb90-bd2a-13ee82f7a5c8@shopzeus.com> <njbdhk$pv2$1@ger.gmane.org> <917ca9c4-aef1-6681-ec70-62de8916117d@shopzeus.com> <njbp5d$mn9$1@ger.gmane.org> <7cdb398f-b3f2-9a3c-b444-2229f8496e2f@shopzeus.com>
Xref csiph.com comp.lang.python:109750

Show key headers only | View raw


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]

Back to comp.lang.python | Previous | Next | Find similar | Unroll thread


Thread

Re: UserList - which methods needs to be overriden? Peter Otten <__peter__@web.de> - 2016-06-09 16:13 +0200

csiph-web