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