Python: how to implement __getattr__()?

后端 未结 8 1838
北海茫月
北海茫月 2020-12-07 22:57

My class has a dict, for example:

class MyClass(object):
    def __init__(self):
        self.data = {\'a\': \'v1\', \'b\': \'v2\'}

Then I

相关标签:
8条回答
  • 2020-12-07 23:21

    You can initialize your class dictionary through the constructor:

        def __init__(self,**data):
    

    And call it as follows:

    f = MyClass(**{'a': 'v1', 'b': 'v2'})
    

    All of the instance attributes being accessed (read) in __setattr__, need to be declared using its parent (super) method, only once:

        super().__setattr__('NewVarName1', InitialValue)
    

    Or

        super().__setattr__('data', dict())
    

    Thereafter, they can be accessed or assigned to in the usual manner:

        self.data = data
    

    And instance attributes not being accessed in __setattr__, can be declared in the usual manner:

        self.x = 1
    

    The overridden __setattr__ method must now call the parent method inside itself, for new variables to be declared:

        super().__setattr__(key,value)
    

    A complete class would look as follows:

    class MyClass(object):
        def __init__(self, **data):
            # The variable self.data is used by method __setattr__
            # inside this class, so we will need to declare it 
            # using the parent __setattr__ method:
            super().__setattr__('data', dict())
            self.data = data            
            # These declarations will jump to
            # super().__setattr__('data', dict())
            # inside method __setattr__ of this class:
            self.x = 1
            self.y = 2
    
        def __getattr__(self, name):
        # This will callback will never be called for instance variables
        # that have beed declared before being accessed.
            if name in self.data:
                # Return a valid dictionary item:
                return self.data[name]
            else:
                # So when an instance variable is being accessed, and
                # it has not been declared before, nor is it contained
                # in dictionary 'data', an attribute exception needs to
                # be raised.
                raise AttributeError
    
        def __setattr__(self, key, value):
            if key in self.data:
                # Assign valid dictionary items here:
                self.data[key] = value
            else:
                # Assign anything else as an instance attribute:
                super().__setattr__(key,value)
    

    Test:

    f = MyClass(**{'a': 'v1', 'b': 'v2'})
    print("f.a = ", f.a)
    print("f.b = ", f.b)
    print("f.data = ", f.data)
    f.a = 'c'
    f.d = 'e'
    print("f.a = ", f.a)
    print("f.b = ", f.b)
    print("f.data = ", f.data)
    print("f.d = ", f.d)
    print("f.x = ", f.x)
    print("f.y = ", f.y)
    # Should raise attributed Error
    print("f.g = ", f.g)
    

    Output:

    f.a =  v1
    f.b =  v2
    f.data =  {'a': 'v1', 'b': 'v2'}
    f.a =  c
    f.b =  v2
    f.data =  {'a': 'c', 'b': 'v2'}
    f.d =  e
    f.x =  1
    f.y =  2
    Traceback (most recent call last):
      File "MyClass.py", line 49, in <module>
        print("f.g = ", f.g)
      File "MyClass.py", line 25, in __getattr__
        raise AttributeError
    AttributeError
    
    0 讨论(0)
  • 2020-12-07 23:23

    Late to the party, but found two really good resources that explain this better (IMHO).

    As explained here, you should use self.__dict__ to access fields from within __getattr__, in order to avoid infinite recursion. The example provided is:

    def __getattr__(self, attrName):
      if not self.__dict__.has_key(attrName):
         value = self.fetchAttr(attrName)    # computes the value
         self.__dict__[attrName] = value
      return self.__dict__[attrName]
    

    Note: in the second line (above), a more Pythonic way would be (has_key apparently was even removed in Python 3):

    if attrName not in self.__dict__:
    

    The other resource explains that the __getattr__ is invoked only when the attribute is not found in the object, and that hasattr always returns True if there is an implementation for __getattr__. It provides the following example, to demonstrate:

    class Test(object):
        def __init__(self):
            self.a = 'a'
            self.b = 'b'
    
        def __getattr__(self, name):
            return 123456
    
    t = Test()
    print 'object variables: %r' % t.__dict__.keys()
    #=> object variables: ['a', 'b']
    print t.a
    #=> a
    print t.b
    #=> b
    print t.c
    #=> 123456
    print getattr(t, 'd')
    #=> 123456
    print hasattr(t, 'x')
    #=> True     
    
    0 讨论(0)
提交回复
热议问题