Перезапись __getattr__ вызывает рекурсию - PullRequest
0 голосов
/ 11 января 2019

Я пытаюсь создать класс-оболочку, а затем перезаписать __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 ]

0 голосов
/ 11 января 2019

Вам нужно изначально установить _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 
0 голосов
/ 11 января 2019

Это

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 в dict экземпляра, но это нарушит наследование, если _foo также является вычисляемым атрибутом - независимо от того, является ли это проблемой или нет, зависит от контекста и конкретного варианта использования.

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

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

...