Question:
I'm trying to figure out when to use __getattr__
and when to __getattribute__
. The documentation mentions that __getattribute__
applies to new style classes. What are New Style Classes?
Translation question Difference between __getattr__ vs __getattribute__ from contributor Yarin
Answer:
Let's take a look at some simple examples of using __getattr__
and __getattribute__
.
__getattr__
Python will call the __getattr__
method whenever you request an attribute that has not yet been defined. In the following example, my Count
class does not have a __getattr__
method. Now in main when I try to access obj1.mymin
and obj1.mymax
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) # 1
print(obj1.mymax) # 10
print(obj1.mycurrent) # AttributeError: 'Count' object has no attribute 'mycurrent'
Now my Count
class has a __getattr__
method. Now when I try to access obj1.mycurrent
python gives me back everything I have implemented in my __getattr__
. In my example, every time I try to call an attribute that doesn't exist, python creates that attribute and sets it to an integer value of 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) # 1
print(obj1.mymax) # 10
print(obj1.mycurrent1) # 0
__getattribute__
Now let's take a look at the __getattribute__
method. If your class has a __getattribute__
method, python calls that method on every attribute whether it exists or not. So why do we need the __getattribute__
method? One good reason is that you can deny access to attributes and make them more secure, as shown in the following example.
Whenever someone tries to access attributes starting with the substring 'cur'
python throws an AttributeError
. Otherwise, it returns this 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)
# либо можете использовать
# return super().__getattribute__(item)
obj1 = Count(1,10)
print(obj1.mymin) # 1
print(obj1.mymax) # 10
print(obj1.current) # AttributeError
Important: to avoid endless recursion in __getattribute__
, its implementation must always call a base class method with the same name to access any attributes it needs. For instance:
object.__getattribute__(self, name)
or
super().__getattribute__(item) # и нет self.__dict__[item]
If your class contains __getattr__
and __getattribute__
magic methods, then __getattribute__
is called __getattribute__
. But if __getattribute__
raises an AttributeError
exception, then the exception will be ignored and the __getattr__
method will be called.
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)
# или вы можете использовать return super().__getattribute__(item)
obj1 = Count(1,10)
print(obj1.mymin) # 1
print(obj1.mymax) # 10
print(obj1.current) # 0
Translation of the answer from participant N Randhawa