Python实现只读属性的核心思路是用私有属性存储值、property提供只读访问,并在__init__中直接赋值私有变量;推荐property+私有属性,可选__slots__增强防护或描述符复用。
Python 中实现“只读属性”但允许在
中赋值,核心思路是:**用私有属性存储值,通过 property 提供只读访问,并在初始化阶段绕过 property 直接写私有变量**。这样既保证实例创建后无法修改,又保留构造时的灵活性。
用 property + 私有属性(推荐)
这是最清晰、最 Pythonic 的方式。关键在于:
property 的 setter 不定义或抛出异常,而直接给底层私有属性(如)赋值
。
示例:
Python 3.14.3微软官方的 Python 扩展,是 VS Code 安装量最高的扩展(209M+)。集成 IntelliSense(通过 Pylance)、调试(通过 Python Debugger)、代码检查、格式化、重构和单元测试等功能。支持 Jupyter Notebook、虚拟环境管理和多 Python 版本切换。下载
立即学习
“
Python免费学习笔记(深入)
”;
使用效果:
→ 成功
→ 返回
→ 抛出
用 __slots__ + property(增强防护)
如果想进一步防止用户通过动态设置
绕过限制,可结合
封锁实例字典:
立即学习
“
Python免费学习笔记(深入)
”;
此时:
仍可行(因为
不限制私有属性写入),但
会报错,且无法新增任意属性干扰逻辑。
用描述符(适合复用场景)
若多个类都需要类似只读字段,可封装成描述符:
class Person:
name = ReadOnly()
立即学习
“
Python免费学习笔记(深入)
”;
注意:该描述符依赖“首次赋值”判断,适用于简单场景;复杂逻辑建议回归 property 方案。
不推荐的做法:重写 __setattr__
虽然可以拦截所有属性赋值,但容易误伤(比如影响
在
中的设置),需额外维护白名单,代码易出错、难维护,一般不建议。
__init____init__self._xclass Person:
def __init__(self, name):
self._name = name # ✅ 允许在 init 中直接赋值
@property
def name(self):
return self._name
# ❌ 不定义 setter,或显式禁止
@name.setter
def name(self, value):
raise AttributeError("name is read-only")p = Person("Alice")p.name"Alice"p.name = "Bob"AttributeErrorp._name = ...__slots__class Person:
__slots__ = ("_name",) # 只允许 _name 属性
def __init__(self, name):
self._name = name
@property
def name(self):
return self._namep._name = "new"__slots__p.age = 10class ReadOnly:
def __init__(self, default=None):
self.default = default
self.name = None # 后续由 __set_name__ 填充
def __set_name__(self, owner, name):
self.name = f"_{name}"
def __get__(self, obj, objtype=None):
if obj is None:
return self
return getattr(obj, self.name, self.default)
def __set__(self, obj, value):
if not hasattr(obj, self.name): # 仅允许第一次设置(即 init 阶段)
setattr(obj, self.name, value)
else:
raise AttributeError(f"{self.name[1:]} is read-only")def __init__(self, name):
self.name = name # ✅ 第一次赋值成功self._name__init__