Domanda

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'
È stato utile?

Soluzione

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)

Altri suggerimenti

Your __setattr__ method is called during the class instantiation when __init__ is running and setting self.current_date and self.prior_date.

To fix this you have to use try, except block around callback calling to prevent the error.

    try:
        for func in self._callbacks[attr]:
            func(value)
    except:
        pass
Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top