Разница между __getattr__ против __getattribute__ - PullRequest
350 голосов
/ 19 июля 2010

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

Ответы [ 5 ]

413 голосов
/ 19 июля 2010

Ключевая разница между __getattr__ и __getattribute__ заключается в том, что __getattr__ вызывается только в том случае, если атрибут не был найден обычными способами.Это хорошо для реализации запасного варианта для отсутствующих атрибутов и, вероятно, является одним из двух, которые вам нужны.

__getattribute__ вызывается перед просмотром реальных атрибутов объекта, и поэтому может быть сложно реализовать правильно.Вы можете легко получить бесконечные рекурсии.

Классы нового стиля происходят от object, классы старого стиля - это классы в Python 2.x без явного базового класса.Но различие между классами старого и нового стиля не является важным при выборе между __getattr__ и __getattribute__.

Вы почти наверняка захотите __getattr__.

99 голосов
/ 05 августа 2016

Давайте посмотрим на несколько простых примеров магических методов __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)
15 голосов
/ 19 июля 2010

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

class SomeObject(object):
    pass

class SubObject(SomeObject):
    pass

Классы старого стиля не:

class SomeObject:
    pass

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

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

14 голосов
/ 16 сентября 2013

Это просто пример, основанный на объяснении Неда Батчелдера .

__getattr__ пример:

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')

И если такой же пример используется с __getattribute__, вы получите >>> RuntimeError: maximum recursion depth exceeded while calling a Python object

6 голосов
/ 19 июля 2010

Классы нового стиля подклассы "объект" (прямо или косвенно). Они имеют __new__ метод класса в дополнение к __init__ и имеют несколько более рациональное поведение на низком уровне.

Обычно вы захотите переопределить __getattr__ (если вы переопределите и то и другое), иначе вам будет сложно поддерживать синтаксис "self.foo" в ваших методах.

Дополнительная информация: http://www.devx.com/opensource/Article/31482/0/page/4

...