Разница между getattr и getattribute


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

5 313

5 ответов:

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

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

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

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

давайте посмотрим несколько простых примеров как __getattr__ и __getattribute__ магические методы.

__getattr__

Python вызовет __getattr__ метод, когда вы запрашиваете атрибут, который еще не был определен. В следующем примере мой класс графа нет __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'

теперь мой класс графа и __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' питон поднимает 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)

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

class SomeObject(object):
    pass

class SubObject(SomeObject):
    pass

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

class SomeObject:
    pass

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

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

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

__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

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

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

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