@artem78

Почему происходит рекурсия в getattr?

Объясните мне, почему возникает рекурсия в методе __getattr__?

class PluginSettings(object):
    def __init__(self, plugin, defaults = {}):
        self._plugin = plugin
        self._defaults = defaults

    def __getattr__(self, attr):
        default = self._defaults.get(attr)
        self._plugin.config.get(attr, default) # Получение свойства из стороннего источника

    def __setattr__(self, attr, value):
        if attr in self.__dict__:
            self.__dict__[attr] = value
        else:
            self._plugin.config.set_key(attr, value) # Сохранение свойства в сторонний источник


class Plugin(BasePlugin):
    def __init__(self, parent, config, name):
        BasePlugin.__init__(self, parent, config, name)

        default_settings = {
            'a': 1,
            'b': 2
        }
        self.settings = PluginSettings(self, default_settings)

plugin = Plugin()
print(plugin.settings.a)
print(plugin.settings.z)


Если убрать __setattr__ это проходит.
  • Вопрос задан
  • 312 просмотров
Решения вопроса 1
@fireSparrow
Когда вы инстанцируете новый экземпляр объект, то это происходит в таком порядке
1. Новый экземпляр создаётся
2. Созданный экземпляр инициализируется в соответствии с __init__

Таким образом, __init__ сработает только после того, как экземпляр уже создан со всеми его методами. Соответственно, и метод __setattr__ (если он объявлен в классе), у этого экземпляра уже есть.

Поэтому, когда в __init__ вы пишете self._defs = defaults интерпретатор уже не будет делать это стандартным способом, а полезет в метод __setattr__.
Там он идёт по ветке else, выполняет pass и на этом успокаивается. В итоге атрибут _defs так и не был записан.

А после этого, когда вы вызываете __getattr__, интерпретатор видит, что он должен обратиться к _defs, пытается найти его, не находит, и опять вызывает __getattr__, чтобы понять, как ему поступить в этом случае. И там он опять видит, что нужно обратиться к _defs. И получается бесконечная рекурсия.

А если __setattr__ не определён, то в __init__ нормально создаётся _defs, и ничего такого не происходит.

Решение:
замените строчку в __init__ на
self.__dict__['_defs'] = defaults
Ответ написан
Пригласить эксперта
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Похожие вопросы