Использование __getattr__ для вызова дочерних методов для определенного типа?

У меня есть класс, поля которого являются экземпляром другого класса.

class Field:
    def get(self):
    def set(self, value):
    def delete(self):

class Document:
    def __init__(self):
        self.y = Field()
        self.z = True

Что я хотел бы сделать, так это когда экземпляр родителя ссылается на свои атрибуты, вместо этого он вызывает дочерние методы.

d = Document()
d.y = 'some value'  # Calls the `set` function of the Field
d.y == 'some value'  # Calls the `get` function of the Field
del d.y  # Calls the `delete` function of the Field

Другая загвоздка в том, что мне нужно такое поведение только тогда, когда поле имеет тип Field.

У меня возникают проблемы с рекурсией, попробуйте использовать __getattr__ и т.п., например:

def __getattr__(self, key):
    if isinstance(getattr(self, key), Field):
        return getattr(self, key).get()
    return getattr(self, key)

Рекурсия довольно очевидна, почему это происходит... но как ее избежать?

Я уже видел несколько примеров на StackOverflow, но не могу понять, как это обойти.


person heiskr    schedule 23.08.2014    source источник
comment
Почему бы просто не использовать дескрипторы? Этот функционал встроен.   -  person univerio    schedule 23.08.2014
comment
То, что вы описываете, в значительной степени совпадает с дескрипторами, за исключением того, что имена методов пишутся __get__, __set__ и __delete__, а дескриптор задается как атрибут класса, а не экземпляра. Я бы посоветовал вам посмотреть некоторую информацию об дескрипторах (например, здесь и здесь).   -  person BrenBarn    schedule 23.08.2014
comment
Спасибо! Я попробую, посмотрим, как пойдет.   -  person heiskr    schedule 23.08.2014
comment
Боковое примечание: если вы не используете Python 3.x, всегда делайте свои классы наследованием от объекта.   -  person jsbueno    schedule 23.08.2014


Ответы (1)


То, что вы описываете, более или менее точно соответствует внутреннему способу доступа Python к атрибутам класса с протоколом дескриптора -

Просто сделайте свои полевые классы методами __get__, __set__ и __delete__, как описано в https://docs.python.org/2/howto/descriptor.html -- И убедитесь, что все ваши классы имеют object в качестве базового класса, иначе они наследуются от классов старого стиля, которые существуют только в Python 2 из соображений совместимости. , и устарели, начиная с Python 2.2 (и полностью игнорируют протокол дескриптора)

person jsbueno    schedule 23.08.2014
comment
Хорошая выноска по базовым классам, я на 2.7. - person heiskr; 23.08.2014
comment
Без использования протокола дескриптора то, о чем вы спрашиваете, может стать довольно сложным. Осуществимо, конечно. - person jsbueno; 23.08.2014