Обработка ошибок в '__getattribute__' и '__getattr__'

Заметка на эти вопросы есть ответы в комментариях

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

Рассмотрим этот класс A. Если экземпляр A имеет атрибут None, доступ к этому атрибуту вызывает AttributeError.

class A:
    def __getattribute__(self, item):
        value = object.__getattribute__(self, item)
        if value is None:
            raise AttributeError('in __getattribute__')

        return value

    def __init__(self, a=None):
        self.a = a

A(1).a  # 1
A().a  # raises AttributeError('in __getattribute__')

Теперь в документах на __getattribute__ говорится

Вызывается безоговорочно для реализации доступа к атрибутам для экземпляров класса. Если класс также определяет __getattr__(), последний не будет вызываться, если только __getattribute__() не вызовет его явно или не вызовет AttributeError.

Исходя из этого, я бы предположил, что любой AttributeError, поднятый в __getattribute__, всегда будет перехвачен, а процесс отложен до __getattr__, так что AttributeError('in __getattribute__') никогда не будет виден вызывающей стороне.

К моему удивлению, не только замечена ошибка, но AttributeError, поднятый на A().a, является тем же в __getattribute__, как если бы __getattr__ никогда не вызывался. Это если __getattr__ не поднимает свой собственный

def __getattr__(self, item):
    raise AttributeError('in __getattr__')

в этом случае мы видим вместо этого AttributeError('in __getattr__').

Какие здесь правила? И применяются ли они, если вместо AttributeError('in __getattribute__') я поднял некоторый подкласс Oops из AttributeError в __getattribute__?

+1
источник поделиться
1 ответ

Обобщая комментарии.

[Python 3.Docs]: модель данных - объект.__getattribute__(self, name) (выделение мое) гласит:

Вызывается безоговорочно для реализации доступа к атрибутам для экземпляров класса. Если класс также определяет __getattr __(), последний не будет вызываться, пока __getattribute __() не вызовет его явно или не вызовет AttributeError. Этот метод должен возвращать (вычисляемое) значение атрибута или вызывать исключение AttributeError. Чтобы избежать бесконечной рекурсии в этом методе, его реализация всегда должна вызывать метод базового класса с тем же именем для доступа к любым необходимым ему атрибутам, например, object.__getattribute__(self, name).

Путаница была вызвана предположением, что __getattr__ унаследован от объекта (что неверно):

>>> dir(object)
['__class__', '__delattr__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__']
>>>
>>> object.__getattribute__
<slot wrapper '__getattribute__' of 'object' objects>
>>> object.__getattr__
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: type object 'object' has no attribute '__getattr__'

Хотя я могу понять, почему некоторых это может сбивать с толку, из того, что меня беспокоит (после многократного просмотра документа):
Если __getattr__ был унаследован от объекта, то текст (цитируемый) "Если класс также определяет __getattr __() "не имеет смысла.

0
источник

Посмотрите другие вопросы по меткам или Задайте вопрос