Python实现只读属性的核心思路是用私有属性存储值、property提供只读访问,并在__init__中直接赋值私有变量;推荐property+私有属性,可选__slots__增强防护或描述符复用。

Python 中实现“只读属性”但允许在 __init__ 中赋值,核心思路是:**用私有属性存储值,通过 property 提供只读访问,并在初始化阶段绕过 property 直接写私有变量**。这样既保证实例创建后无法修改,又保留构造时的灵活性。
用 property + 私有属性(推荐)
这是最清晰、最 Pythonic 的方式。关键在于:property 的 setter 不定义或抛出异常,而 __init__ 直接给底层私有属性(如 self._x)赋值。
示例:
class 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")
立即学习“Python免费学习笔记(深入)”;
使用效果:
-
p = Person("Alice")→ 成功 -
p.name→ 返回"Alice" -
p.name = "Bob"→ 抛出AttributeError
用 __slots__ + property(增强防护)
如果想进一步防止用户通过动态设置 p._name = ... 绕过限制,可结合 __slots__ 封锁实例字典:
class Person:
__slots__ = ("_name",) # 只允许 _name 属性
def __init__(self, name):
self._name = name
@property
def name(self):
return self._name立即学习“Python免费学习笔记(深入)”;
此时:p._name = "new" 仍可行(因为 __slots__ 不限制私有属性写入),但 p.age = 10 会报错,且无法新增任意属性干扰逻辑。
用描述符(适合复用场景)
若多个类都需要类似只读字段,可封装成描述符:
class 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")class Person:
name = ReadOnly()
def __init__(self, name):
self.name = name # ✅ 第一次赋值成功立即学习“Python免费学习笔记(深入)”;
注意:该描述符依赖“首次赋值”判断,适用于简单场景;复杂逻辑建议回归 property 方案。
不推荐的做法:重写 __setattr__
虽然可以拦截所有属性赋值,但容易误伤(比如影响 self._name 在 __init__ 中的设置),需额外维护白名单,代码易出错、难维护,一般不建议。










