动态添加Python中的@property
我知道我可以通过一些方法动态地给一个对象添加实例方法,比如这样:
import types
def my_method(self):
# logic of method
# ...
# instance is some instance of some class
instance.my_method = types.MethodType(my_method, instance)
之后我可以调用 instance.my_method()
,这时候 self 会正确绑定,一切都能正常工作。
现在,我的问题是:怎么做才能得到和用 @property 装饰新方法一样的效果呢?
我猜可能是这样:
instance.my_method = types.MethodType(my_method, instance)
instance.my_method = property(instance.my_method)
但是,这样做的话 instance.my_method
返回的是一个属性对象。
2 个回答
4
因为这个问题并不是只问如何给特定的实例添加属性,所以可以使用以下方法给整个类添加一个属性,这样这个属性就会对这个类的所有实例可用,具体效果可能会有所不同。
cls = type(my_instance)
cls.my_prop = property(lambda self: "hello world")
print(my_instance.my_prop)
# >>> hello world
注意:我再补充一个答案,因为我觉得@Alex Martelli的回答虽然正确,但他是通过创建一个新类来保存这个属性来实现的,而我的这个答案旨在更直接、简单,不把事情抽象成自己的方法。
39
要想让property
描述符生效,它必须放在类里面,而不是放在实例里面。如果你不想改变现有的类,以免影响其他实例的行为,那你就需要创建一个“每个实例专用的类”,比如:
def addprop(inst, name, method):
cls = type(inst)
if not hasattr(cls, '__perinstance'):
cls = type(cls.__name__, (cls,), {})
cls.__perinstance = True
inst.__class__ = cls
setattr(cls, name, property(method))
我给这些特别的“每个实例专用的类”加了一个属性,这样如果你在同一个实例上多次调用addprop
时,就不需要重复创建多个类了。
需要注意的是,像使用property
一样,你需要确保使用的类是新式的(通常是通过直接或间接继承object
来获得),而不是古老的遗留风格(在Python 3中被淘汰),后者是默认分配给没有基类的类的。