You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Python动态添加只读property问题:实例设置为何返回property对象?

问题原因

property是类属性,只有定义在类层面时,Python的属性访问机制才会触发它的getter/setter逻辑。当你通过self.__setattr__给实例设置属性时,这个property对象只是作为普通的实例属性被存储,访问时直接返回对象本身,不会触发getter。而notMyName是定义在类上的property,所以访问时会正常调用getter。

解决方案

方案一:动态修改类属性

既然property需要在类层面生效,那就在实例初始化时,直接给类添加这个property属性,而不是给实例加。这样所有实例都会共享这个类级别的property,访问时就会触发getter:

def property_factory(name):
    def getter(self):
        return self.__getattribute__(name)

    def setter(self, value):
        raise Exception('Cannot set a value')

    return property(getter, setter)

class read_file(object):
    def __init__(self):
        self.name = 'myName'
        self.value = 'myValue'

class a(object):
    def __init__(self):
        list1 = read_file()
        # 先设置私有实例属性
        self.__setattr__('_' + list1.name, list1.value)
        # 动态给类添加property
        setattr(a, list1.name, property_factory('_' + list1.name))

# 测试
b = a()
print(b.myName)  # 输出 'myValue'
try:
    b.myName = 'true'
except Exception as e:
    print(e)  # 输出 'Cannot set a value'

注意:如果多个实例初始化时会添加同名的类属性,后面的会覆盖前面的,适用于每个实例添加的属性名唯一的场景。

方案二:重写__getattr__和__setattr__

如果不想修改类属性,可以重写类的__getattr__和__setattr__方法,自己管理只读属性的映射:

def property_factory(name):
    def getter(self):
        return self.__getattribute__(name)

    def setter(self, value):
        raise Exception('Cannot set a value')

    return property(getter, setter)

class read_file(object):
    def __init__(self):
        self.name = 'myName'
        self.value = 'myValue'

class a(object):
    def __init__(self):
        self._read_only_attrs = {}  # 存储只读属性名和对应的私有属性名
        list1 = read_file()
        private_name = '_' + list1.name
        self.__setattr__(private_name, list1.value)
        self._read_only_attrs[list1.name] = private_name

    def __getattr__(self, name):
        # 当属性不存在时触发,检查是否是只读属性
        if name in self._read_only_attrs:
            private_name = self._read_only_attrs[name]
            return self.__getattribute__(private_name)
        raise AttributeError(f"'a' object has no attribute '{name}'")

    def __setattr__(self, name, value):
        # 检查是否是只读属性,如果是则抛出异常
        if hasattr(self, '_read_only_attrs') and name in self._read_only_attrs:
            raise Exception('Cannot set a value')
        # 其他属性正常设置
        super().__setattr__(name, value)

# 测试
b = a()
print(b.myName)  # 输出 'myValue'
try:
    b.myName = 'true'
except Exception as e:
    print(e)  # 输出 'Cannot set a value'

这种方法不会修改类属性,每个实例维护自己的只读属性列表,更灵活,适合每个实例有不同只读属性的场景。

补充说明

  • 方案一中,如果你不想影响其他实例,可以考虑创建类的子类,在子类上添加property后实例化子类,但这样会增加类的数量,按需选择即可。
  • 方案二中,__getattr__只有在访问不存在的实例属性时才会触发,所以如果有同名的类属性,会优先访问类属性,这点需要注意。

内容的提问来源于stack exchange,提问作者Mezzerine

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.21 01:02:33