Understanding the difference between __getattr__ and __getattribute__

后端 未结 4 839
深忆病人
深忆病人 2020-11-29 14:44

I am trying to understand the difference between __getattr__ and __getattribute__, however, I am failing at it.

The answer to the Stack Ove

4条回答
  •  清酒与你
    2020-11-29 15:12

    __getattribute__ is called whenever an attribute access occurs.

    class Foo(object):
        def __init__(self, a):
            self.a = 1
    
        def __getattribute__(self, attr):
            try:
                return self.__dict__[attr]
            except KeyError:
                return 'default'
    f = Foo(1)
    f.a
    

    This will cause infinite recursion. The culprit here is the line return self.__dict__[attr]. Let's pretend (It's close enough to the truth) that all attributes are stored in self.__dict__ and available by their name. The line

    f.a
    

    attempts to access the a attribute of f. This calls f.__getattribute__('a'). __getattribute__ then tries to load self.__dict__. __dict__ is an attribute of self == f and so python calls f.__getattribute__('__dict__') which again tries to access the attribute '__dict__'. This is infinite recursion.

    If __getattr__ had been used instead then

    1. It never would have run because f has an a attribute.
    2. If it had run, (let's say that you asked for f.b) then it would not have been called to find __dict__ because it's already there and __getattr__ is invoked only if all other methods of finding the attribute have failed.

    The 'correct' way to write the above class using __getattribute__ is

    class Foo(object):
        # Same __init__
    
        def __getattribute__(self, attr):
            return super(Foo, self).__getattribute__(attr)
    

    super(Foo, self).__getattribute__(attr) binds the __getattribute__ method of the 'nearest' superclass (formally, the next class in the class's Method Resolution Order, or MRO) to the current object self and then calls it and lets that do the work.

    All of this trouble is avoided by using __getattr__ which lets Python do it's normal thing until an attribute isn't found. At that point, Python hands control over to your __getattr__ method and lets it come up with something.

    It's also worth noting that you can run into infinite recursion with __getattr__.

    class Foo(object):
        def __getattr__(self, attr):
            return self.attr
    

    I'll leave that one as an exercise.

提交回复
热议问题