Подтвердить что ты не робот

Объект "супер", не вызывающий __getattr__

У меня есть один объект, завернутый в другой. "Wrapper" обращается к атрибутам из "Обернутого" объекта, переопределяя __getattr__. Это хорошо работает, пока мне не нужно переопределить атрибут на подклассе, а затем получить доступ к атрибуту из базового класса с помощью super().

Я все еще могу получить доступ к атрибуту непосредственно из __getattr__, но почему super() не работает?

class Wrapped(object):
    def __init__(self, value):
        self.value = value

    def hello_world(self):
        print 'hello world', self.value

class Wrapper(object):
    def __init__(self, obj):
        self.wrapped_obj = obj

    def __getattr__(self, name):
        if name in self.__dict__:
            return getattr(self, name)
        else:
            return getattr(self.wrapped_obj, name)

class Subclass(Wrapper):
    def __init__(self, obj):
        super(Subclass, self).__init__(obj)

    def hello_world(self):
        # this works
        func = super(Subclass, self).__getattr__('hello_world')()
        # this doesn't
        super(Subclass, self).hello_world()

a = Wrapped(2)
b = Subclass(a)
b.hello_world()
4b9b3361

Ответ 1

В соответствии с этим, super не разрешает неявные вызовы функций "hook", таких как __getattr__. Я не уверен, почему это реализовано таким образом (вероятно, есть веская причина, и вещи уже запутывают достаточно, поскольку у супер-объекта есть пользовательские методы __getattribute__ и __get__, как есть), но похоже, что это просто путь вещи есть.

Изменить: Похоже, что это сообщение немного очистило. Похоже, проблема заключается в том, что дополнительный слой косвенности, вызванный __getattribute__, игнорируется при вызове функций неявно. Выполнение foo.x эквивалентно

foo.__getattr__(x)

(Предполагая, что метод __getattribute__ не задан и x не находится в foo.__dict__) Однако это НЕ эквивалентно

foo.__getattribute__('__getattr__')(x)

Так как super возвращает прокси-объект, у него есть дополнительный слой косвенности, который заставляет вещи терпеть неудачу.

P.S. Проверка self.__dict__ в вашей функции __getattr__ совершенно не нужна. __getattr__ вызывается только в том случае, если атрибут еще не существует в вашем dict. (Используйте __getattribute__, если вы хотите, чтобы он всегда вызывался, но тогда вы должны быть очень осторожны, потому что даже что-то простое, как if name in self.__dict__, вызовет бесконечную рекурсию.