Setting an item from dictionary x
is a completely different sort of operation from setting x
-- it is not modifying the Foo
instance itself but rather it is modifying the dictionary to which Foo
contains a reference.
What you could do is when you set x
, you could set it to an instance of a subclass of dict which overrides __setitem__
in whatever way you want. In this example, it would run the specified callback function before setting the item.
class MyDict(dict):
"""
a MyDict is like a dict except that when you set an item, before
doing so it will call a callback function that was passed in when the
MyDict instance was created
"""
def __init__(self, value, setitem_callback=None, *args, **kwargs):
super(MyDict, self).__init__(value, *args, **kwargs)
self._setitem_callback = setitem_callback
def __setitem__(self, key, value):
if self._setitem_callback:
self._setitem_callback(key, value)
super(MyDict, self).__setitem__(key, value)
class Foo(object):
def __init__(self):
self._x = None
@property
def x(self):
print "Getting attribute x"
return self._x
def _x_setitem(self, key, value):
print "Setting key {} of x".format(key)
@x.setter
def x(self, value):
print "Setting attribute x"
if isinstance(value, dict):
# caller tries to set it to a dict, but we set it to a MyDict instance instead
value = MyDict(value, setitem_callback=self._x_setitem)
self._x = value
test = Foo()
test.x = {'A':1, 'B':2}
test.x['A'] = 3
print test.x
Gives:
Setting attribute x
Getting attribute x
Setting key A of x
Getting attribute x
{'A': 3, 'B': 2}