跳转至

描述符协议的属性查找优先级是怎样的?property 的底层原理是什么?

🔍 描述符的属性查找,其实就一条铁序: 数据描述符(有 set) > 实例 dict > 非数据描述符(只有 get


🗺️ 查找路线图

image.png

重点:只要在类里定义了 set(或 delete),这个描述符就成了“闸门”。哪怕你在实例字典里硬塞同名属性,访问时也完全不理你,直接走描述符。


🧬 @property 的底层:一个 C 实现的“闸门”

class Dog:
    @property
    def name(self):
        return self._name

    @name.setter
    def name(self, val):
        self._name = val

这里的 name 本质上是一个 property 对象,它内置了 getset,所以是数据描述符。

用纯 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)

因为 MyPropertyset,所以它的实例在类里一站,实例字典就别想插队:

d = Dog()
d._name = "Rex"
print(d.name)           # 调 getter

d.__dict__['name'] = "Fido"  # 试图绕开
print(d.name)           # 依然是 Rex,因为数据描述符优先

🎯 优先级实战速记

查看内嵌表格

@property 落座第一档,所以才能把属性访问死死捏在手里。


💡 真正有用的结论:如果你想让一个懒加载属性被实例覆盖,就用非数据描述符;如果想绝对控制(比如校验、ORM 字段),就用数据描述符——@property 就是现成的、最顺手的那把锁。