Dynamically adding a property to a class

人走茶凉 提交于 2020-01-04 06:25:07

问题


This has been previously asked on Stack Overflow, but none of the answers seem to address exactly what I need to do. In my case, I want these dynamically-added properties to be a shortcut to store and read values from a database, so unfortunately it's not as easy as in this answer (where a lambda function was used) or this one (where values where stored in a dictionary): I must call other methods of the class.

This is my attempt:

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

The last line, however, raises:

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

Any ideas on how to achieve this?


回答1:


I don't know why you use operator.methodcaller here. When you call f.spam=2, it will invoke setter. setter = operator.methodcaller('set_value', name) means setter(r) = r.set_value(name). Make no sense in your case.

I suggest you write this way, using @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

If this helped you, please confirm it as the answer. Thanks!




回答2:


Modifying class template looks a bit odd for me. I would suggest to overload __getattr__() and __setattr__() methods in your case.

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')


来源:https://stackoverflow.com/questions/24509737/dynamically-adding-a-property-to-a-class

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!