I'm trying to implement a simple model class that executes callbacks whenever an attribute is set in the class, however I am getting an AttributeError when trying to use this in my application. It seems the instance doesn't have the attributes I setup in my initializer and I'm a bit confused as to why. I haven't used magic methods much, so some elaboration might be helpful:
class ReportModel(object):
def __init__(self):
self.current_date = None
self.prior_date = None
self._callbacks = defaultdict([])
def __setattr__(self, attr, value):
object.__setattr__(self, attr, value)
for func in self._callbacks[attr]:
func(value)
def set_callback(self, attr, function):
self._callbacks[attr].append(function)
Traceback:
AttributeError: 'ReportModel' object has no attribute '_callbacks'
Since you have overridden __setattr__()
, when you do self.current_date = None
in __init__()
it will call your __setattr__()
implementation which will try to access self._callbacks
. This is what causes the AttributeError.
Here is one option to fix this:
class ReportModel(object):
def __init__(self):
object.__setattr__(self, '_callbacks', defaultdict(list))
self.current_date = None
self.prior_date = None
def __setattr__(self, attr, value):
object.__setattr__(self, attr, value)
for func in self._callbacks[attr]:
func(value)
def set_callback(self, attr, function):
self._callbacks[attr].append(function)
This adds the _callbacks
attribute using object.__setattr__()
before any other attributes are added, so that future calls to __setattr__()
for your ReportModel
instance will not choke on accessing self._callbacks
.
Alternatively, you could modify __setattr__()
to check for the existence of the _callbacks
attribute:
class ReportModel(object):
def __init__(self):
object.__setattr__(self, '_callbacks', defaultdict(list))
self.current_date = None
self.prior_date = None
def __setattr__(self, attr, value):
object.__setattr__(self, attr, value)
if hasattr(self, '_callbacks'):
for func in self._callbacks[attr]:
func(value)
def set_callback(self, attr, function):
self._callbacks[attr].append(function)