Difference between __getattr__ vs __getattribute__

前端 未结 8 1064
甜味超标
甜味超标 2020-11-22 12:54

I am trying to understand when to use __getattr__ or __getattribute__. The documentation mentions __getattribute__ applies to new-sty

相关标签:
8条回答
  • 2020-11-22 13:02

    I find that no one mentions this difference:

    __getattribute__ has a default implementation, but __getattr__ does not.

    class A:
        pass
    a = A()
    a.__getattr__ # error
    a.__getattribute__ # return a method-wrapper
    

    This has a clear meaning: since __getattribute__ has a default implementation, while __getattr__ not, clearly python encourages users to implement __getattr__.

    0 讨论(0)
  • 2020-11-22 13:03

    New-style classes inherit from object, or from another new style class:

    class SomeObject(object):
        pass
    
    class SubObject(SomeObject):
        pass
    

    Old-style classes don't:

    class SomeObject:
        pass
    

    This only applies to Python 2 - in Python 3 all the above will create new-style classes.

    See 9. Classes (Python tutorial), NewClassVsClassicClass and What is the difference between old style and new style classes in Python? for details.

    0 讨论(0)
  • 2020-11-22 13:05

    A key difference between __getattr__ and __getattribute__ is that __getattr__ is only invoked if the attribute wasn't found the usual ways. It's good for implementing a fallback for missing attributes, and is probably the one of two you want.

    __getattribute__ is invoked before looking at the actual attributes on the object, and so can be tricky to implement correctly. You can end up in infinite recursions very easily.

    New-style classes derive from object, old-style classes are those in Python 2.x with no explicit base class. But the distinction between old-style and new-style classes is not the important one when choosing between __getattr__ and __getattribute__.

    You almost certainly want __getattr__.

    0 讨论(0)
  • 2020-11-22 13:06

    This is just an example based on Ned Batchelder's explanation.

    __getattr__ example:

    class Foo(object):
        def __getattr__(self, attr):
            print "looking up", attr
            value = 42
            self.__dict__[attr] = value
            return value
    
    f = Foo()
    print f.x 
    #output >>> looking up x 42
    
    f.x = 3
    print f.x 
    #output >>> 3
    
    print ('__getattr__ sets a default value if undefeined OR __getattr__ to define how to handle attributes that are not found')
    

    And if same example is used with __getattribute__ You would get >>> RuntimeError: maximum recursion depth exceeded while calling a Python object

    0 讨论(0)
  • 2020-11-22 13:11

    Lets see some simple examples of both __getattr__ and __getattribute__ magic methods.

    __getattr__

    Python will call __getattr__ method whenever you request an attribute that hasn't already been defined. In the following example my class Count has no __getattr__ method. Now in main when I try to access both obj1.mymin and obj1.mymax attributes everything works fine. But when I try to access obj1.mycurrent attribute -- Python gives me AttributeError: 'Count' object has no attribute 'mycurrent'

    class Count():
        def __init__(self,mymin,mymax):
            self.mymin=mymin
            self.mymax=mymax
    
    obj1 = Count(1,10)
    print(obj1.mymin)
    print(obj1.mymax)
    print(obj1.mycurrent)  --> AttributeError: 'Count' object has no attribute 'mycurrent'
    

    Now my class Count has __getattr__ method. Now when I try to access obj1.mycurrent attribute -- python returns me whatever I have implemented in my __getattr__ method. In my example whenever I try to call an attribute which doesn't exist, python creates that attribute and set it to integer value 0.

    class Count:
        def __init__(self,mymin,mymax):
            self.mymin=mymin
            self.mymax=mymax    
    
        def __getattr__(self, item):
            self.__dict__[item]=0
            return 0
    
    obj1 = Count(1,10)
    print(obj1.mymin)
    print(obj1.mymax)
    print(obj1.mycurrent1)
    

    __getattribute__

    Now lets see the __getattribute__ method. If you have __getattribute__ method in your class, python invokes this method for every attribute regardless whether it exists or not. So why we need __getattribute__ method? One good reason is that you can prevent access to attributes and make them more secure as shown in the following example.

    Whenever someone try to access my attributes that starts with substring 'cur' python raises AttributeError exception. Otherwise it returns that attribute.

    class Count:
    
        def __init__(self,mymin,mymax):
            self.mymin=mymin
            self.mymax=mymax
            self.current=None
    
        def __getattribute__(self, item):
            if item.startswith('cur'):
                raise AttributeError
            return object.__getattribute__(self,item) 
            # or you can use ---return super().__getattribute__(item)
    
    obj1 = Count(1,10)
    print(obj1.mymin)
    print(obj1.mymax)
    print(obj1.current)
    

    Important: In order to avoid infinite recursion in __getattribute__ method, its implementation should always call the base class method with the same name to access any attributes it needs. For example: object.__getattribute__(self, name) or super().__getattribute__(item) and not self.__dict__[item]

    IMPORTANT

    If your class contain both getattr and getattribute magic methods then __getattribute__ is called first. But if __getattribute__ raises AttributeError exception then the exception will be ignored and __getattr__ method will be invoked. See the following example:

    class Count(object):
    
        def __init__(self,mymin,mymax):
            self.mymin=mymin
            self.mymax=mymax
            self.current=None
    
        def __getattr__(self, item):
                self.__dict__[item]=0
                return 0
    
        def __getattribute__(self, item):
            if item.startswith('cur'):
                raise AttributeError
            return object.__getattribute__(self,item)
            # or you can use ---return super().__getattribute__(item)
            # note this class subclass object
    
    obj1 = Count(1,10)
    print(obj1.mymin)
    print(obj1.mymax)
    print(obj1.current)
    
    0 讨论(0)
  • 2020-11-22 13:15

    New-style classes are ones that subclass "object" (directly or indirectly). They have a __new__ class method in addition to __init__ and have somewhat more rational low-level behavior.

    Usually, you'll want to override __getattr__ (if you're overriding either), otherwise you'll have a hard time supporting "self.foo" syntax within your methods.

    Extra info: http://www.devx.com/opensource/Article/31482/0/page/4

    0 讨论(0)
提交回复
热议问题