网易首页 > 网易号 > 正文 申请入驻

Python:描述符协议

0
分享至

在 Python 面向对象编程中,类属性的访问和控制非常灵活。为了支持属性的动态访问、验证、缓存和代理行为,Python 提供了描述符协议(Descriptor Protocol),是 Python 中实现访问控制、延迟加载、ORM 字段、property 装饰器等机制的核心基础。

描述符提供了比传统 getter/setter 更统一、更强大的能力,是 Python 对象模型中最重要的机制之一。理解描述符,能够让你真正掌握 Python 的属性访问机制。

一、传统的 Setter/Getter/Deleter

假设我们有一个类 BankAccount,希望控制银行账户余额 _balance 的访问方式:

        del self._balance

使用方式:

# account.set_balance(-50)              # 会抛出 ValueError

为什么说这是“传统的”?

• 必须显式调用 get_x / set_x,不够 Pythonic。

• 无法用自然的属性语法(obj.attr)。

• 冗长、繁琐,不符合“简单即美”的 Python 哲学。

因此,Python 提供了更高级的方案 —— @property。

二、Python 的进化:@property 装饰器

@property 允许你用属性语法访问方法:

        del self._balance

使用方式:

del account.balance                # 输出:Deleting balance!

我们也可以不使用装饰器语法,而是显式创建 property:

    balance = property(get_balance, set_balance, del_balance)

装饰器是语法糖,本质和显式写法等价!

三、描述符与描述符协议

property 不是魔法,它基于描述符协议(Descriptor Protocol)。

(1)什么是描述符

只要一个对象实现了以下任意方法,它就是描述符:

• __get__(self, instance, owner)

• __set__(self, instance, value)

• __delete__(self, instance)

并且当此对象作为类属性存在时,通过 obj.attr 访问会自动调用这些方法。描述符允许我们“钩住”属性访问过程,自定义其行为。

(2)描述符协议三方法

__get__(self, instance, owner)

读取属性时触发。

参数:

self:描述符实例本身。

instance:通过实例访问时为实例,通过类访问时为 None。

owner:拥有描述符的类(通过类访问时)。

__set__(self, instance, value)

设置属性时触发。

__delete__(self, instance)

删除属性时触发。

所有 property、方法绑定、ORM 字段、cached_property 都基于描述符协议。

(3)property 与描述符的关系

当我们使用 @property 时,本质上是在创建一个 property 描述符实例。

以下是 property 的简化原理:

        self.fdel(instance)

property 就是一个实现了完整描述符协议的类。

property 默认是“非数据描述符”;只有在定义了 fset 或 fdel 时才成为“数据描述符”。

四、数据描述符与非数据描述符

Python 将描述符分为两类,它们的优先级不同。

(1)数据描述符(Data Descriptor)

指的是定义了 __set__ 或 __delete__ 的描述符。

如:

• property(具有 setter 或 deleter)

• 自定义描述符实现了 __set__

• ORM 字段、typed 属性验证描述符

优先级:数据描述符优先于实例属性。

示例:

print(a.x)   # get   —— 实例属性不会覆盖数据描述符

(2)非数据描述符(Non-Data Descriptor)

指的是只实现了 __get__ 的描述符。

如:

• 普通方法(function)

• property(无 setter / deleter)

优先级:实例属性优先于非数据描述符。

示例:

print(a.x)  # 100  —— 实例字典覆盖了非数据描述符

优先级总结:

数据描述符 > 实例属性 > 非数据描述符 > 普通类属性

示例:

print(obj.non_data_desc)       # 输出: "实例属性"(实例属性优先)

五、属性查找顺序

当执行 obj.attr 时,Python 实际执行(简化逻辑):

1、在类(type(obj)) 中查找 attr;如果找到且是数据描述符 → 调用其 __get__ 并返回结果。

2、否则查找实例字典 obj.__dict__;如果存在 → 返回该值。

3、若类属性是非数据描述符 → 调用其 __get__ 并返回结果。

4、否则返回类属性本身。

5、若都找不到 → 如果对象实现了 __getattr__ 则调用它。

设置属性时:

• 若存在数据描述符 → 调用其 __set__

• 否则写入实例字典

删除属性时:

• 若存在数据描述符 → 调用其 __delete__

• 否则从实例字典删除

示例:完整的查找规则演示

print(demo.class_attr)  # 类属性

这种精细的属性查找顺序与描述符优先级机制,使得 Python 在实现面向对象特性时既灵活又高效。

六、自定义描述符

下面构造一个完整的年龄验证描述符,改进 __get__ 的容错处理,同时演示推荐的实例数据存储方式(使用实例字典并带上唯一键)并加入 __set_name__ 支持以获取属性名:

将其绑定到类属性:

使用示例:

(1)描述符实例是类属性,不是实例属性

描述符对象只创建一次(在类定义时),不应该在描述符内部用 self.xxx 来存储每个实例的数据,否则所有实例会共享同一份数据(通常这是错误的)。正确做法是将实例数据存储在 instance.__dict__ 或使用 instance 上的独立键(例如 _{name})。

(2)set_name(推荐)

自 Python 3.6 起,描述符可以实现 __set_name__(self, owner, name),类创建时会被调用一次,这可以帮助描述符自动记录它在类中对应的属性名,从而简化向 instance.__dict__ 中存储值的实现(见上面示例)。这是实现“按属性名存储”“不会冲突”的推荐方式。

(3)删除属性后再访问

del p.age 会调用 __delete__,如果我们在 __get__ 中直接访问 instance.__dict__ 中的键而该键不存在,会抛出 AttributeError。在实际实现中可以选择更友好的行为(例如返回 None、抛出带信息的异常或触发延迟加载)。

七、描述符的典型应用场景

描述符在 Python 中有广泛的应用。

(1)数据验证与类型检查

(2)延迟加载与缓存机制(如 cached_property)

(3)观察者模式与属性监听

(4)权限控制与访问审计

(5)ORM 与数据映射(字段描述符)

(6)配置管理与依赖注入

补充说明:方法如何绑定为 bound method

Python 中的函数对象(定义在类体中的函数)实现了 __get__(即函数对象是非数据描述符),当通过实例访问时,function.__get__(instance, owner) 会返回一个“绑定方法”(bound method),它把该实例作为第一个参数(self)封装到函数上。理解这一点有助于把“方法也是描述符”与前面描述符优先级的讨论连起来。

小结

描述符协议是 Python 中控制属性访问的底层机制。只要一个类实现了 __get__、__set__ 或 __delete__,它就能作为描述符拦截属性的读取、写入和删除操作。property、方法绑定、ORM 字段以及许多高级特性都依赖描述符协议。

理解描述符的关键在于区分数据描述符和非数据描述符,并掌握它们在属性查找顺序中的不同优先级。__set_name__ 是现代描述符实现中非常有用的钩子(Python 3.6+),推荐在自定义描述符中使用它来管理实例字典的键。

通过自定义描述符,我们可以构建高度可复用、可扩展的属性管理逻辑,使得类的行为更加灵活与优雅;描述符不仅是 Python 的底层机制,更是构建大型系统与框架的基础组件。


点赞有美意,赞赏是鼓励

特别声明:以上内容(如有图片或视频亦包括在内)为自媒体平台“网易号”用户上传并发布,本平台仅提供信息存储服务。

Notice: The content above (including the pictures and videos if any) is uploaded and posted by a user of NetEase Hao, which is a social media platform and only provides information storage services.

相关推荐
热点推荐
搞笑,快船队解说员给一位即将吃掉自己推文的纸条的球迷提了建议

搞笑,快船队解说员给一位即将吃掉自己推文的纸条的球迷提了建议

好火子
2026-01-27 06:53:33
32岁凯恩获拜仁天价续约,背后是6500万解约条款的恐慌

32岁凯恩获拜仁天价续约,背后是6500万解约条款的恐慌

浮萍足球
2026-01-27 00:53:05
翟欣欣邻居曝猛料:她被带走时哭疯了,父母跟着落泪,称跟她无关

翟欣欣邻居曝猛料:她被带走时哭疯了,父母跟着落泪,称跟她无关

谈史论天地
2026-01-26 18:40:03
央媒痛批,沉寂七十年,从同志到戏子,资本的獠牙终于露出来了

央媒痛批,沉寂七十年,从同志到戏子,资本的獠牙终于露出来了

锋哥与八卦哥
2026-01-24 11:50:17
居民楼出现一条50米长裂缝,“里面结构都露出来了” 业主认为与底层商铺烟管爆炸有关

居民楼出现一条50米长裂缝,“里面结构都露出来了” 业主认为与底层商铺烟管爆炸有关

大风新闻
2026-01-26 10:54:04
2015年复旦林森浩被执行死刑,行刑前却安慰父亲:爸爸,没事的

2015年复旦林森浩被执行死刑,行刑前却安慰父亲:爸爸,没事的

谈史论天地
2026-01-13 11:04:56
闲鱼不愧是全国最大的黑市,网民:只有你想不到,没有你买不到

闲鱼不愧是全国最大的黑市,网民:只有你想不到,没有你买不到

小熊侃史
2026-01-16 07:40:07
药师提醒:银杏叶片、血塞通、复方丹参片,心脑血管用药别再选错

药师提醒:银杏叶片、血塞通、复方丹参片,心脑血管用药别再选错

蜉蝣说
2026-01-17 18:36:03
后悔也晚了!大批F35逼到家门口,伊朗却发现红旗9和歼10还未到位

后悔也晚了!大批F35逼到家门口,伊朗却发现红旗9和歼10还未到位

文雅笔墨
2026-01-25 03:11:20
身价百亿,坐拥北京一条街,出门私人飞机,京圈顶级富婆天团来了

身价百亿,坐拥北京一条街,出门私人飞机,京圈顶级富婆天团来了

泠泠说史
2026-01-26 14:19:11
性行为缺失会促癌?华中大最新:性行为缺失会削弱抗癌免疫力,保持性行为则有利于抗癌

性行为缺失会促癌?华中大最新:性行为缺失会削弱抗癌免疫力,保持性行为则有利于抗癌

医诺维
2026-01-26 17:02:36
央视紧急预警2026!3种高端菜是毒坑,孩子绝对不能吃

央视紧急预警2026!3种高端菜是毒坑,孩子绝对不能吃

辉哥说动漫
2026-01-26 12:26:01
中国阳谋奏效!特朗普心态已崩,连夜通告全球:要给中国上手段?

中国阳谋奏效!特朗普心态已崩,连夜通告全球:要给中国上手段?

透视到底
2026-01-27 06:39:12
30岁女子洗澡时摸到颈部肿大淋巴结确诊肺癌后选择轻生,丈夫无奈:我们本来要二胎!

30岁女子洗澡时摸到颈部肿大淋巴结确诊肺癌后选择轻生,丈夫无奈:我们本来要二胎!

消化石医生
2025-11-29 11:38:20
警报!三种“毒早餐”竟成家长的心头好,快来看看你在喂什么!

警报!三种“毒早餐”竟成家长的心头好,快来看看你在喂什么!

特约前排观众
2026-01-25 00:20:06
委内瑞拉代总统:我“受够了”来自华盛顿的命令

委内瑞拉代总统:我“受够了”来自华盛顿的命令

扬子晚报
2026-01-26 12:40:21
李亚鹏曝光捐赠名单,向太发声:我捐了几百万,王菲默默支持了几千万,也是不够的……

李亚鹏曝光捐赠名单,向太发声:我捐了几百万,王菲默默支持了几千万,也是不够的……

都市快报橙柿互动
2026-01-24 19:48:42
号召国人要沉住气,胡锡进又喝假酒了

号召国人要沉住气,胡锡进又喝假酒了

林中木白
2026-01-25 17:14:01
陈毅之子陈丹淮求学时遇同学攀比家世,被问及父亲情况,他回应:我父亲只是个处长

陈毅之子陈丹淮求学时遇同学攀比家世,被问及父亲情况,他回应:我父亲只是个处长

文史明鉴
2026-01-26 18:00:13
美国赶走大批中国科学家后,聘用了印度专家,结果如何?

美国赶走大批中国科学家后,聘用了印度专家,结果如何?

素年文史
2026-01-26 19:11:06
2026-01-27 07:16:49
MediaTea
MediaTea
专业的数字媒体、新媒体技术
1726文章数 72关注度
往期回顾 全部

科技要闻

印奇再上牌桌,阶跃融资50亿

头条要闻

女子被丈夫和闺蜜背叛一夜白头:听到儿子叫第三者妈妈

头条要闻

女子被丈夫和闺蜜背叛一夜白头:听到儿子叫第三者妈妈

体育要闻

叛逆的大公子,要砸了贝克汉姆这块招牌

娱乐要闻

张雨绮被抵制成功!辽视春晚已将她除名

财经要闻

从美式斩杀线看中国社会的制度韧性构建

汽车要闻

宾利第四台Batur敞篷版发布 解锁四项定制创新

态度原创

游戏
健康
房产
本地
公开课

猎魂世界:先遣服1.5版本新内容汇总!这第7魂环真是够那个了!

耳石脱落为何让人天旋地转+恶心?

房产要闻

突发!三亚官宣,调整安居房政策!

本地新闻

云游中国|格尔木的四季朋友圈,张张值得你点赞

公开课

李玫瑾:为什么性格比能力更重要?

无障碍浏览 进入关怀版