Вопрос

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

Это было полезно?

Решение

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

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

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

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

Другие советы

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

__getattr__

Python вызовет __getattr__ метод всякий раз, когда вы запрашиваете атрибут, который еще не был определен.В следующем примере мой класс Подсчитывать не имеет никакого __getattr__ способ.Теперь в main, когда я пытаюсь получить доступ к обоим 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__ способ?Одна из веских причин заключается в том, что вы можете запретить доступ к атрибутам и сделать их более безопасными, как показано в следующем примере.

Всякий раз, когда кто-то пытается получить доступ к моим атрибутам, которые начинаются с substring '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__ повышает 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)

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

__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

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

class SomeObject(object):
    pass

class SubObject(SomeObject):
    pass

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

class SomeObject:
    pass

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

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

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

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

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

Изучая печатную плату Beazley & Jones, я наткнулся на четкий и практичный пример использования для __getattr__ это помогает ответить на часть вопроса OP "когда".Из книги:

"Тот __getattr__() метод - это что-то вроде универсального средства для поиска атрибутов.Это метод, который вызывается, если код пытается получить доступ к несуществующему атрибуту ". Мы знаем это из приведенных выше ответов, но в рецепте PCB 8.15 эта функциональность используется для реализации шаблон проектирования делегирования.Если объект A имеет атрибут Object B, который реализует множество методов, которым Объект A хочет делегировать, вместо переопределения всех методов объекта B в объекте A только для вызова методов объекта B, определите __getattr__() способ заключается в следующем:

def __getattr__(self, name):
    return getattr(self._b, name)

где _b - имя атрибута объекта A, который является объектом B.Когда метод, определенный для объекта B, вызывается для объекта A, __getattr__ метод будет вызван в конце цепочки поиска.Это также сделало бы код более чистым, поскольку у вас нет списка методов, определенных только для делегирования другому объекту.

  • получить атрибут:Используется для извлечения атрибута из экземпляра.Он фиксирует каждую попытку доступа к атрибуту экземпляра с помощью точечной нотации или встроенной функции getattr().
  • getattr ( получить ):Выполняется как последний ресурс, когда атрибут не найден в объекте.Вы можете выбрать возврат значения по умолчанию или повышение значения AttributeError.

Возвращаясь к __гетатрибут__ функция;если реализация по умолчанию не была переопределена;при выполнении метода выполняются следующие проверки:

  • Проверьте, существует ли дескриптор с таким же именем (имя атрибута), определенный в любом классе в цепочке MRO (разрешение объекта метода).
  • Затем просматривает пространство имен экземпляра
  • Затем просматривает пространство имен класса
  • Затем в пространство имен каждой базы и так далее.
  • Наконец, если не найден, реализация по умолчанию вызывает запасной вариант getattr ( получить )() метод экземпляра, и он вызывает исключение AttributeError в качестве реализации по умолчанию.

Это фактический реализация объекта.__метод getattribute__:

"..c: функция::PyObject* PyObject_GenericGetAttr(PyObject *o, PyObject *имя) Универсальная функция получения атрибута, предназначенная для помещения в тип слот tp_getattro объекта.Он ищет дескриптор в словаре классов в MRO объекта, а также атрибут в объекте :attr:~object .диктовать (если присутствует).Как описано в: ссылка: дескрипторы, дескрипторы данных отдают предпочтение атрибутам экземпляра, в то время как не-данные дескрипторы этого не делают.В противном случае возникает ошибка :exc:AttributeError ."

Я нахожу, что никто не упоминает об этой разнице:

__getattribute__ имеет реализацию по умолчанию, но __getattr__ не делает этого.

class A:
    pass
a = A()
a.__getattr__ # error
a.__getattribute__ # return a method-wrapper

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

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top