python __getattribute__ RecursionError при возврате атрибута переменной класса - PullRequest
1 голос
/ 24 апреля 2020

Почему Foo2 приводит к бесконечной рекурсии, вызывающей getattr переменной класса в __getattribute__, но Foo отлично работает, делая тот же вызов в __getattr__? Любые предложения о том, как заставить Foo2 работать?

class Foobar(object):
    def __init__(self):
        super().__init__()
        self.bar = 5

    def getbar(self):
        return self.bar


class Foo(object):
    def __init__(self):
        super().__init__()
        self.__foo = Foobar()

    def __getattr__(self, attr):
        return getattr(self.__foo, attr)


class Foo2(object):
    def __init__(self):
        super().__init__()
        self.__foo = Foobar()

    def __getattribute__(self, attr):
        try:
            return getattr(self.__foo, attr)
        except AttributeError:
            super().__getattribute__(attr)


if __name__ == '__main__':
    foo = Foo()
    foo2 = Foo2()
    print(foo.bar, foo.getbar())  # Works as expected
    try:
        print(foo2.bar, foo2.getbar())  # Doesn't work
    except RecursionError:
        print('Why does Foo2 result in RecursionError. How to fix?')

Настройка: Windows 10, Python 3.7

1 Ответ

3 голосов
/ 24 апреля 2020

Метод __getattribute__ вызывается безоговорочно для поиска всех атрибутов объекта, а не только тех, которые не существуют (что делает __getattr__). Когда вы делаете self.__foo в его реализации, вы повторяете, поскольку __foo - это еще один атрибут, который мы пытаемся найти на объекте.

Чтобы избежать этой проблемы, вам нужно вызвать * вашего родителя * Метод 1007 *, чтобы получить все свои собственные атрибуты внутри метода __getattribute__:

def __getattribute__(self, attr):
    try:
        return getattr(super().__getattribute__("_Foo__foo"), attr)
    except AttributeError:
        super().__getattribute__(attr)

Обратите внимание, что мне пришлось вручную применить искажение имени к атрибуту __foo, потому что нам нужно передать имя как строка в super().__getattribute__. Это, вероятно, говорит о том, что вы не должны делать искажения в первую очередь. Лучше всего выбрать имя с одним подчеркиванием.

...