Давайте посмотрим на несколько простых примеров магических методов __getattr__
и __getattribute__
.
__getattr__
Python будет вызывать метод __getattr__
всякий раз, когда вы запрашиваете атрибут, который не имеетт уже определены.В следующем примере мой класс Count не имеет метода __getattr__
.Теперь в основном, когда я пытаюсь получить доступ к атрибутам obj1.mymin
и obj1.mymax
, все работает нормально.Но когда я пытаюсь получить доступ к атрибуту obj1.mycurrent
- Python дает мне 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'
Теперь мой класс Count имеет метод __getattr__
.Теперь, когда я пытаюсь получить доступ к атрибуту obj1.mycurrent
, python возвращает мне все, что я реализовал в своем методе __getattr__
.В моем примере всякий раз, когда я пытаюсь вызвать атрибут, который не существует, python создает этот атрибут и устанавливает для него целочисленное значение 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__
Теперь давайте посмотрим__getattribute__
метод.Если у вас есть метод __getattribute__
в вашем классе, python вызывает этот метод для каждого атрибута независимо от того, существует он или нет.Так зачем нам метод __getattribute__
?Хорошая причина в том, что вы можете запретить доступ к атрибутам и сделать их более безопасными, как показано в следующем примере.
Всякий раз, когда кто-то пытается получить доступ к моим атрибутам, начинается с подстроки 'cur' pythonповышает AttributeError
исключениеВ противном случае он возвращает этот атрибут.
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)
Важно: Чтобы избежать бесконечной рекурсии в методе __getattribute__
, его реализация всегда должна вызывать метод базового класса с тем же именем для доступа к любым необходимым ему атрибутам.Например: object.__getattribute__(self, name)
или super().__getattribute__(item)
, а не self.__dict__[item]
ВАЖНО
Если ваш класс содержит оба getattr и getattribute магические методытогда __getattribute__
вызывается первым.Но если __getattribute__
вызывает AttributeError
исключение, то исключение будет проигнорировано и будет вызван метод __getattr__
.Смотрите следующий пример:
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)