动态向类添加属性

2 投票
2 回答
694 浏览
提问于 2025-04-18 11:43

这个问题之前在Stack Overflow上也有人问过,但似乎没有一个答案能完全解决我想做的事情。在我的情况下,我希望这些动态添加的属性能够作为一个快捷方式,用来从数据库中存储和读取值,所以这并不像这个答案(使用了lambda函数)或这个答案(把值存储在字典里)那么简单:我必须调用类的其他方法。

这是我的尝试:

import operator

class Foo(object):

    def get_value(self, name):
        # read and return value from database
        return -1

    def set_value(self, name, value):
        # store value in database
        pass

def add_metadata_property(name):
    getter = operator.methodcaller('get_value', name)
    setter = operator.methodcaller('set_value', name) # gets value at runtime
    setattr(Foo, name, property(getter, setter))

add_metadata_property('spam')
f = Foo()
f.spam # works!
f.spam = 2

然而,最后一行却引发了:

Traceback (most recent call last):
File "<stdin>", line 27, in <module>
TypeError: methodcaller expected 1 arguments, got 2

有没有什么想法可以实现这个?

2 个回答

1

修改类模板对我来说有点奇怪。我建议你在这种情况下重载(也就是重新定义)__getattr__()__setattr__() 这两个方法。

class Foo:
    def __getattr__(self, name):
        print('read and return value from database for ', name)
        return 123
    def __setattr__(self, name, value):
        print('store value', value, 'for', name, 'in database')
4

我不明白你为什么在这里使用 operator.methodcaller。当你调用 f.spam=2 时,它会触发一个设置器(setter)。setter = operator.methodcaller('set_value', name) 的意思是 setter(r) = r.set_value(name)。在你的情况下,这没有意义。

我建议你这样写,使用 @classmethod

class Foo(object):

    @classmethod
    def get_value(self, name):
        # read and return value from database
        return -1
    @classmethod
    def set_value(self,  name, value):
        # store value in database
        pass

def add_metadata_property(name):
    setattr(Foo, name, property(Foo.get_value, Foo.set_value))

add_metadata_property('spam')
f = Foo()
f.spam # works!
f.spam = 2

如果这对你有帮助,请确认这是答案。谢谢!

撰写回答