Почему использование __dict__ в __setattr__ вызывает бесконечный цикл в __getattr__

Я написал рабочую программу для этого указанного упрощенного класса Mesh, но я не могу заставить ее работать для реального класса с десятками методов/свойств. Я не могу изменить реальный класс Mesh, и я не могу сделать расширенный Mesh класса Object.

Это отлично работает:


    class Mesh:
        def __init__(self):
            self.hide_render = False


    class Object:
        def __init__(self, mesh_):
            self.mesh = mesh_

        def __getattr__(self, item):
            return self.mesh.__getattribute__(item) # infinite loop in this line

        def __setattr__(self, name, value):
            if name == 'hide_render': # line to replace----------
                self.mesh.__setattr__(name, value)
            else:
                super().__setattr__(name, value)

    ob = Object(Mesh())

    print(ob.hide_render)
    print(ob.mesh.hide_render)
    ob.mesh.hide_render = True
    print(ob.hide_render)
    print(ob.mesh.hide_render)
    ob.hide_render = False
    print(ob.hide_render)
    print(ob.mesh.hide_render)

Output:

    False
    False
    True
    True
    False
    False

Но когда я хочу сделать то же самое для реального класса Mesh с гораздо большим, чем атрибут «hide _render», заменив первую строку в методе setattr на: if name not in self.__dict__: или if name in self.mesh.__dict__:

Я получаю бесконечный цикл в методе getattr. Почему? И как это решить?


person Piotr Kowalczyk    schedule 09.01.2019    source источник
comment
Как вы думаете, что влияет на настройку self.mesh?   -  person user2357112 supports Monica    schedule 09.01.2019


Ответы (1)


Ваши проблемы возникают, когда self.mesh не существует. Если вы пытаетесь отложить все поиски объектов, которые еще не существуют в self.__dict__, в self.mesh, вы столкнетесь с проблемой, когда не сможете выполнить поиск или присвоить самому self.mesh.

Есть несколько способов исправить это. Вы можете использовать self.__dict__['mesh'] или super().__setattr__ вызов из __init__ вместо прямого присваивания. Или вы могли бы выделить имя mesh в __setattr__:

class Object:
    def __init__(self, mesh):
        self.mesh = mesh

    def __getattr__(self, name):
        return getattr(self.mesh, name)

    def __setattr__(self, name, value):
        if name in self.__dict__ or name == 'mesh':   # special case for 'mesh' here!
            super().__setattr__(name, value)
        else:
            setattr(self.mesh, name, value)
person Blckknght    schedule 09.01.2019
comment
Это не то, что я искал в конце концов. Смысл использования dict заключался в том, чтобы сделать ненужными избыточные исправления для каждого добавленного свойства. Я имею в виду, когда я добавлю self.something = something в метод init, мне нужно будет добавить что-то в качестве еще одного особого случая в setattr. Как этого избежать? - person Piotr Kowalczyk; 20.01.2019
comment
Использование теста name in self.__dict__ работает только для атрибутов, которые уже есть в словаре экземпляра. Прежде всего, чтобы поместить их в словарь, нужно сделать еще кое-что. Без дополнительной информации я не могу подробно рассказать о своем ответе, но вы, возможно, могли бы определить список уровней классов или набор имен, которые вы бы проверили. Попробуйте что-то вроде name in self.list_of_names вместо name == "mesh". - person Blckknght; 25.01.2019