Как динамически добавить свойство в класс? - PullRequest
182 голосов
/ 25 августа 2009

Цель состоит в том, чтобы создать фиктивный класс, который будет вести себя как набор результатов db.

Так, например, если запрос к базе данных возвращает, используя выражение dict, {'ab':100, 'cd':200}, то я хотел бы видеть:

>>> dummy.ab
100

Сначала я подумал, может быть, я мог бы сделать это так:

ks = ['ab', 'cd']
vs = [12, 34]
class C(dict):
    def __init__(self, ks, vs):
        for i, k in enumerate(ks):
            self[k] = vs[i]
            setattr(self, k, property(lambda x: vs[i], self.fn_readyonly))

    def fn_readonly(self, v)
        raise "It is ready only"

if __name__ == "__main__":
    c = C(ks, vs)
    print c.ab

но c.ab возвращает объект свойства.

Замена строки setattr на k = property(lambda x: vs[i]) совершенно бесполезна.

Итак, как правильно создать свойство экземпляра во время выполнения?

P.S. Мне известна альтернатива, представленная в Как используется метод __getattribute__?

Ответы [ 21 ]

0 голосов
/ 29 апреля 2014

Недавно я столкнулся с подобной проблемой, решение, которое я нашел, использует __getattr__ и __setattr__ для свойств, которые я хочу обработать, все остальное передается оригиналам.

class C(object):
    def __init__(self, properties):
        self.existing = "Still Here"
        self.properties = properties

    def __getattr__(self, name):
        if "properties" in self.__dict__ and name in self.properties:
            return self.properties[name] # Or call a function, etc
        return self.__dict__[name]

    def __setattr__(self, name, value):
        if "properties" in self.__dict__ and name in self.properties:
            self.properties[name] = value
        else:
            self.__dict__[name] = value

if __name__ == "__main__":
    my_properties = {'a':1, 'b':2, 'c':3}
    c = C(my_properties)
    assert c.a == 1
    assert c.existing == "Still Here"
    c.b = 10
    assert c.properties['b'] == 10
...