动态添加Python中的@property

22 投票
2 回答
15715 浏览
提问于 2025-04-15 23:26

我知道我可以通过一些方法动态地给一个对象添加实例方法,比如这样:

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中被淘汰),后者是默认分配给没有基类的类的。

撰写回答