Перезапись __getattr__ вызывает рекурсию

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

class A(object):
    def __init__(self):
        pass

    def __getattr__(self, name):
        print 'HERE'
        return getattr(self.foo, name)

    @property
    def foo(self):
        try:
            return self._foo
        except:
            self._foo = [2, 1]
            return self._foo 

Я хотел бы сделать что-то вроде

 a = A()
 a.sort()  # initializes and sorts _foo 
 print a.foo

какие отпечатки [1, 2]

Наконец мне нужно _foo инициализировать когда foo вызывается в первый раз или когда кто-то пытается получить атрибут от A() это не в A, К сожалению, я вхожу в какую-то рекурсию и HERE печатается много раз. Возможно ли то, что я хочу?

2 ответа

Решение

Это

return getattr(self.foo, name)

на самом деле:

foo = self.foo
return getattr(foo, name)

Сейчас self.foo на самом деле назвать это:

try:
   return self._foo

который, так как ваш класс определяет __getattr__ и - хотя бы один первый звонок self.foo - не имеет _foo атрибут, звонки self.__getattr__("_foo"), который вызывает self.fooи т. д. и т. д. и т. д.

Простое решение: убедитесь, что self._foo определяется раньше (т.е. в инициализаторе) вместо того, чтобы пытаться определить его в foo(),

Вы также можете проверить на наличие _foo в инстанции, но это сломает наследство, если _foo также является вычисляемым атрибутом - зависит ли это от проблемы или нет, зависит от контекста и конкретного варианта использования.

@property
def foo(self):
    if '_foo' not in self.__dict__:
        self._foo = [2, 1]
    return self._foo 

NB: я полагаю, вы сделали foo вычисляемый атрибут для ленивой инициализации _fooиначе это имеет мало смысла. Если это так, вы также можете, довольно просто, создать _foo в инициализаторе со значением часового (обычно Noneесли None является допустимым значением для этого атрибута) и дать ему реальное значение в foo(),

Вам нужно установить _foo изначально какой-то ценности, даже если она не настоящая. Как это:

class A(object):
    _foo = None

    def __init__(self):
        pass

    def __getattr__(self, name):
        print 'HERE'
        return getattr(self.foo, name)

    @property
    def foo(self):
        if self._foo is None:
            self._foo = [2, 1]
        return self._foo 
Другие вопросы по тегам