Let's imagine I have a single class X. The purpose of X is to wrap a list or dict and provide event-listening capabilities. All works well.
class X(object):
    def __init__(self, obj)
        self._obj = obj
    def __getattr__(self, name):
        # do stuff with self._obj
    def __getitem__(self, key):
        return self._obj[key]
    def __setitem__(self, key, val):
        self._obj[key] = val
    # rest of functionality ...
So this can be used to wrap a dict like so:
x = X({
    'foo' : False
})
x.listen('foo', callback)
X['foo'] = True         # triggers event
X.update({
    'foo' : False       # triggers event
})
Or a list:
x = X([1,2])
x.listen(callback)
X.append(1)        # triggers event
X[0] = 10          # triggers event
Great. Almost to what I wanted to accomplish ...
Now the current issue is that, because X is for both list and dict objects, it can't inherit from either. This means I don't have the magic class functions, such as __contains__.
Which leads code like this
d = X({
        'foo' : True    
    })
    if 'foo' in d:
        print 'yahoo!'
Throwing a KeyError.
How can I work around this without defining every magic method I need inside of X. If I did it this way, for each of those definitions I would have to write two return values based off whether self._obj is a list or dict.
I thought I could do this with metaclasses at first but that doesn't seem to be a solution, since I need access to the values being passed to check whether it's a dict or list.
An easy way would be to use a proxy class, for example wrapt.ObjectProxy. It will behave exactly like the "proxied" class except for the overridden methods. However instead of self._obj you can simply use self.__wrapped__ to access the "unproxied" object.
from wrapt import ObjectProxy
class Wrapper(ObjectProxy):
    def __getattr__(self, name):
        print('getattr')
        return getattr(self.__wrapped__, name)
    def __getitem__(self, key):
        print('getitem')
        return self.__wrapped__[key]
    def __setitem__(self, key, val):
        print('setitem')
        self.__wrapped__[key] = val
    def __repr__(self):
        return repr(self.__wrapped__)
This behaves like a dict if you wrap a dict:
>>> d = Wrapper({'foo': 10})
>>> d['foo']
getitem
10
>>> 'foo' in d   # "inherits" __contains__
True
and like a list, if a list is wrapped:
>>> d = Wrapper([1,2,3])
>>> d[0]
getitem
1
>>> for i in d:   # "inherits" __iter__
...     print(i)
1
2
3
                        If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With