[Python] Разница между __getattr__ vs __getattribute__


Answers

Давайте посмотрим на некоторые простые примеры методов магии __getattr__ и __getattribute__ .

__getattr__

Python будет вызывать метод __getattr__ всякий раз, когда вы запрашиваете атрибут, который еще не был определен. В следующем примере класс класса не имеет метода __getattr__ . Теперь, когда я пытаюсь получить доступ к obj1.mymin и obj1.mymax все работает отлично. Но когда я пытаюсь получить доступ obj1.mycurrent атрибуту 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 атрибуту 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__ . Если у вас есть метод __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)
Question

Я пытаюсь понять, когда использовать __getattr__ или __getattribute__ . В documentation упоминается __getattribute__ применяется к классам нового стиля. Что такое классы нового стиля?




Классы нового стиля наследуются от object или из другого нового класса стиля:

class SomeObject(object):
    pass

class SubObject(SomeObject):
    pass

В классах старого стиля нет:

class SomeObject:
    pass

Это применимо только к Python 2 - в Python 3 все вышеперечисленное создаст классы нового стиля.

См. 9. Классы (учебник Python), NewClassVsClassicClass и какова разница между старым стилем и новыми классами стилей в Python? для деталей.






Links