描述符协议的属性查找优先级是怎样的?property 的底层原理是什么?
🔍 描述符的属性查找,其实就一条铁序:
数据描述符(有 set) > 实例 dict > 非数据描述符(只有 get)
🗺️ 查找路线图¶

重点:只要在类里定义了 set(或 delete),这个描述符就成了“闸门”。哪怕你在实例字典里硬塞同名属性,访问时也完全不理你,直接走描述符。
🧬 @property 的底层:一个 C 实现的“闸门”¶
class Dog:
@property
def name(self):
return self._name
@name.setter
def name(self, val):
self._name = val
这里的 name 本质上是一个 property 对象,它内置了 get 和 set,所以是数据描述符。
用纯 Python 可以描出它的骨架:
class MyProperty:
def __init__(self, fget=None, fset=None):
self.fget = fget
self.fset = fset
def __get__(self, obj, objtype=None):
if obj is None:
return self
return self.fget(obj)
def __set__(self, obj, value):
if self.fset is None:
raise AttributeError("read-only")
self.fset(obj, value)
def setter(self, fset):
return type(self)(self.fget, fset)
因为 MyProperty 有 set,所以它的实例在类里一站,实例字典就别想插队:
d = Dog()
d._name = "Rex"
print(d.name) # 调 getter
d.__dict__['name'] = "Fido" # 试图绕开
print(d.name) # 依然是 Rex,因为数据描述符优先
🎯 优先级实战速记¶
@property 落座第一档,所以才能把属性访问死死捏在手里。
💡 真正有用的结论:如果你想让一个懒加载属性被实例覆盖,就用非数据描述符;如果想绝对控制(比如校验、ORM 字段),就用数据描述符——@property 就是现成的、最顺手的那把锁。