Страницы

Поиск по вопросам

четверг, 13 февраля 2020 г.

Может ли базовый класс знать что-то про методы потомков?

#python #наследование #методы


Разбираюсь сейчас с наследованием в Python, а также с магическими методами. Имеется код:

class Researcher:
    def __getattr__(self, name):
        return 'Nothing found :()\n'

    def __getattribute__(self, name):
        print('Looking for {}'.format(name))
        return super().__getattribute__(name)


obj = Researcher()

print(obj.attr)
print(obj.method)
print(obj.DFG2H3J00KLL)


Возник вопрос: откуда объект object() (который создался после вызова super()) знает
о методе __getattr__ класса Researcher? Разве базовый класс знает что-то про методы
потомков?
    


Ответы

Ответ 1



super() вовсе не возвращает просто объект родительского класса. В этом легко убедиться, если перед строчкой где вы его вызываете поставить print(super()). Если такое проделать, то код в этом месте выведет: , > Как видите, это не экземпляр родительского класса object. Это особый объект-обёртка, который управляет вызовом методов из классов выше по иерархии для текущего объекта. И вот эта обёртка и знает всё и про методы Researcher, и про всех его родителей. UPD: Действительно, как правильно отметил автор вопроса в комментариях, если заменить super() на object(), то код всё равно работает. Но это уже не связано с наследованием, а связано с нюансами работы __getattr__ и __getattribute__ Дело в том, что __getattribute__, если он реализован, то он срабатывает для вообще всех обращений к атрибутам - как к существующим, так и к несуществующим. В то время, как __getattr__ срабатывает только для обращения к несуществующим атрибутам - то есть тогда, когда при обращении к атрибуту произошло исключение AttributeError. То есть вот что происходит, когда вы меняете super на object: Код делает обращение к атрибуту объекта. Так как это объект класса Researcher, для которого есть метод __getattribute__, то код выполняет именно этот метод. Внутри него идёт обращение к object().__getattribute__(name) Для экземпляра класса object обращение к аттрибуту работает стандартно, то есть для неизвестного аттрибута, такого как DFG2H3J00KLL будет выброшено исключение AttributeError Т.к. при возникновении этого исключение всегда проверяется, есть ли у объекта __getattr__, и так как всё это происходит всё ещё для экземпляра Researcher, а у него этот метод есть, то именно он и срабатывает. Проиллюстрировать это можно следующими примерами. Демонстрация того, что исключение AttributeError действительно выбрасывается: class Researcher: def __getattr__(self, name): return 'Nothing found :()\n' def __getattribute__(self, name): print('Looking for {}'.format(name)) try: object().__getattribute__(name) except Exception as e: print('!!! ', type(e)) obj = Researcher() print(obj.attr) print(obj.method) print(obj.DFG2H3J00KLL) Демонстрация того, что даже если мы не будем обращаться ни к object, ни к super, а просто руками выбросим AttributeError, то всё будет работать так же, как и изначально: class Researcher: def __getattr__(self, name): return 'Nothing found :()\n' def __getattribute__(self, name): print('Looking for {}'.format(name)) raise AttributeError obj = Researcher() print(obj.attr) print(obj.method) print(obj.DFG2H3J00KLL)

Комментариев нет:

Отправить комментарий