python中的描述符是什么?

news/2025/11/11 12:43:35/文章来源:https://www.cnblogs.com/wangya216/p/19209784

描述符:从数据、非数据到内置装饰器

一、简介

简单来说,描述符就是 Python 里一种“懂规矩的工具类”——这里的“规矩”就是描述符协议,只要一个类实现了 __get__(取值)、__set__(赋值)、__delete__(删除)这三个特殊方法中的至少一个,它就成了描述符,能帮我们控制属性的访问逻辑。

打个比方,描述符就像属性的“管家”:有的管家能管“拿东西”和“放东西”(数据描述符),有的只能管“拿东西”(非数据描述符),还有些是 Python 自带的“现成管家”(内置装饰器),不用我们自己调教就能用。接下来就分三条线,用具体例子讲清楚这三类“管家”。

二、第一条线:数据描述符(能管“读+写”的全能管家)

数据描述符是“全能管家”,因为它同时实现了 __get____set__ 方法,既能控制属性的“取值”,也能控制“赋值”,特别适合需要给属性加“限制条件”的场景。

通俗例子:给“年龄”加个“不能为负”的限制

比如我们要定义一个“Person”类,其中“age”属性必须是正整数,负数不允许赋值。这时候用数据描述符就能轻松实现:

class PositiveInt:# 记录属性名(比如"age")def __set_name__(self, owner, name):self.name = name# 取值:从实例的__dict__里拿属性值def __get__(self, instance, owner):return instance.__dict__.get(self.name, 0)  # 没值时默认返回0# 赋值:先验证,符合条件才存def __set__(self, instance, value):if not isinstance(value, int) or value < 0:raise ValueError("年龄必须是正整数!")instance.__dict__[self.name] = value# 用数据描述符定义Person类的age属性
class Person:age = PositiveInt()  # age指向数据描述符实例# 测试:符合条件的赋值能成功,不符合的会报错
p = Person()
p.age = 25  # 没问题,存进去了
print(p.age)  # 25(触发__get__)p.age = -5  # 报错:ValueError: 年龄必须是正整数!

这里的 PositiveInt 就是数据描述符——它像管家一样,每次给 age 赋值前都会“检查”,确保值是正整数,完美实现了属性的访问控制。

三、第二条线:非数据描述符(只管“读”的半程管家)

非数据描述符是“半程管家”,它只实现了 __get__ 方法,只能控制“取值”逻辑,没法管“赋值”和“删除”。适合做“只读属性”或“每次访问都自动计算”的属性。

通俗例子1:固定的“商品默认折扣”

比如电商系统里,所有商品的默认折扣都是 0.9(九折),这个值不能改,用非数据描述符就很合适:

class FixedDiscount:def __init__(self, discount):self.discount = discount  # 初始化固定折扣# 取值:每次访问都返回固定折扣def __get__(self, instance, owner):return self.discount# 用非数据描述符定义商品类的默认折扣
class Goods:default_discount = FixedDiscount(0.9)  # 默认九折# 测试:取值能拿到固定值,赋值不生效
apple = Goods()
print(apple.default_discount)  # 0.9(触发__get__)# 尝试改折扣,看似没报错,但实际没生效
apple.default_discount = 0.8
print(apple.default_discount)  # 还是0.9(非数据描述符没__set__,赋值只存在实例字典里,不影响__get__)

通俗例子2:每次访问都更“新”的当前时间

再比如需要一个“当前时间”属性,每次访问都要拿到最新的时间,而非数据描述符能做到“实时计算”:

import timeclass CurrentTime:# 取值:每次访问都重新获取当前时间def __get__(self, instance, owner):return time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())# 用非数据描述符定义“时间工具类”
class TimeTool:now = CurrentTime()# 测试:两次访问时间不一样
tool = TimeTool()
print(tool.now)  # 第一次访问:2024-10-01 14:30:00
time.sleep(2)    # 等2秒
print(tool.now)  # 第二次访问:2024-10-01 14:30:02(时间更新了)

这里的 CurrentTime 就是非数据描述符——它不管赋值,只负责每次取值时“算一次最新时间”,很适合这类“动态计算”的场景。

四、第三条线:内置装饰器(Python自带的“现成管家”)

前面两种描述符都需要我们自己写类、实现 __get__/__set__,但 Python 早就帮我们封装好了一批“现成管家”——就是 @property@classmethod@staticmethod 这三个内置装饰器,它们本质都是简化版的描述符,不用我们手动写特殊方法,直接用装饰器就能实现常用功能。

通俗例子1:@property——快速做“计算属性”(含@属性名.setter)

@property 默认是“只读属性”,但通过 @属性名.setter 装饰器,我们能给属性加“赋值逻辑”,实现“可读可写”且带验证的属性,相当于简化版的数据描述符。

比如定义一个“学生”类,“分数”属性需要满足:取值时返回分数,赋值时必须是 0-100 的整数,超出范围就报错:

class Student:def __init__(self, name):self.name = name# 用下划线开头的变量存实际值,避免和@property装饰的属性名冲突self._score = 0# 1. 定义“取值”逻辑:@property装饰的方法是“ getter ”@propertydef score(self):print(f"获取{self.name}的分数")return self._score# 2. 定义“赋值”逻辑:@score.setter装饰的方法是“ setter ”@score.setterdef score(self, value):# 赋值前先验证:必须是整数,且在0-100之间if not isinstance(value, int):raise TypeError("分数必须是整数!")if value < 0 or value > 100:raise ValueError("分数必须在0-100之间!")print(f"给{self.name}设置分数:{value}")self._score = value# 测试:取值和赋值都触发对应的逻辑
s = Student("小明")
# 赋值:触发@score.setter装饰的方法
s.score = 95  # 打印:给小明设置分数:95
# 取值:触发@property装饰的方法
print(s.score)  # 打印:获取小明的分数 → 输出95# 测试错误情况
s.score = 105  # 报错:ValueError: 分数必须在0-100之间!
s.score = "优秀"  # 报错:TypeError: 分数必须是整数!

这里要注意两个关键点:

  1. 实际存储值用 _score(下划线开头,约定为“私有变量”),避免和 @property 装饰的 score 重名,否则会触发无限递归;
  2. @score.setter 必须跟在 @property 后面,名字要和 @property 装饰的方法一致(都是 score),才能绑定到同一个属性上。

本质上,@property + @属性名.setter 就是 Python 帮我们封装了数据描述符的 __get____set__ 方法,不用自己写完整的描述符类,就能实现带验证的读写属性。

通俗例子2:@classmethod——“用类调用的方法”

比如需要一个“从字符串创建日期对象”的功能,方法要和“类”绑定,而不是和实例绑定,用 @classmethod 就行:

class Date:def __init__(self, year, month, day):self.year = yearself.month = monthself.day = day# 用@classmethod定义“类方法”,第一个参数是cls(代表类本身)@classmethoddef from_str(cls, date_str):year, month, day = map(int, date_str.split("-"))return cls(year, month, day)  # 用cls创建实例# 测试:直接用类调用方法,不用先创建实例
date1 = Date.from_str("2024-10-01")
print(date1.year, date1.month, date1.day)  # 2024 10 1

@classmethod 本质是个非数据描述符,它把方法的“调用者”绑定到类上,让方法能直接操作类相关的逻辑。

通俗例子3:@staticmethod——“类里的工具函数”

比如类里需要一个“判断数字是否为偶数”的工具函数,不用访问类或实例的属性,用 @staticmethod 最合适:

class MathTool:# 用@staticmethod定义“静态方法”,没有cls或self参数@staticmethoddef is_even(num):return num % 2 == 0# 测试:类和实例都能调用,不用依赖类/实例属性
print(MathTool.is_even(4))  # True(类调用)
mt = MathTool()
print(mt.is_even(5))        # False(实例调用)

@staticmethod 也是个非数据描述符,它更像“挂在类里的普通函数”,不用绑定类或实例,单纯做工具逻辑。

五、描述符的常见认识误区

很多人刚学描述符时会搞混概念,这里澄清三个最常见的误区:

误区1:描述符是“标识符”

澄清:标识符是变量名、类名这种“名字标签”(比如 agePerson),而描述符是“有功能的工具类/实例”(比如 PositiveInt 类、PositiveInt() 实例)——前者是“名字”,后者是“名字指向的东西”,本质完全不同。

比如 class Person: age = PositiveInt() 中:

  • age 是标识符(给属性起的名字);
  • PositiveInt() 是描述符实例(真正控制 age 访问的“管家”);
  • 关系是:age 这个“名字”,指向了描述符实例这个“管家”,而不是描述符本身是标识符。

误区2:只有自定义类才是描述符

澄清:前面讲的 @property@classmethod@staticmethod 这三个内置装饰器,本质都是 Python 封装好的描述符——它们已经帮我们实现了 __get__ 等方法,只是不用我们手动写而已,属于“现成的描述符”。

比如 @property 装饰的方法,底层会生成一个 property 类的实例,而 property 类本身就实现了 __get____set__(当用了 @属性名.setter 时),是标准的数据描述符。

误区3:非数据描述符完全不能“写”属性

澄清:非数据描述符只是没实现 __set__,没法自定义“赋值逻辑”,但不是不能给属性赋值——赋值会直接把值存到实例的 __dict__(实例自己的字典)里,只是不会触发描述符的任何逻辑。

比如前面的 FixedDiscount 例子:

apple = Goods()
apple.default_discount = 0.8  # 赋值不会触发描述符的__set__(因为没有)
print(apple.__dict__)  # {'default_discount': 0.8}(值存在实例字典里)
print(apple.default_discount)  # 还是0.9(访问时优先用描述符的__get__,忽略实例字典的 value)

简单说:非数据描述符的“不能写”,是“不能按自定义逻辑写”,不是“完全不能赋值”。

六、三者对比(帮你快速选“管家”)

类型 实现方法要求 能控制的操作 通俗用例 核心优势
数据描述符 必须有__get__+set 读、写(可加验证) 年龄(不能负)、工资(有下限) 能严格控制属性赋值,适合强约束场景
非数据描述符 只有__get__ 只读(可动态计算) 固定折扣、实时时间 实现简单,适合只读/动态计算场景
内置装饰器 不用自己实现方法 按装饰器功能定 @property+@score.setter控分数、@classmethod创建实例 开箱即用,不用写复杂描述符类
(@property可加@属性名.setter)

简单总结:如果需要强约束(比如验证)且想自定义完整逻辑,选数据描述符;如果只是只读/动态计算,选非数据描述符;如果是日常简单的读写控制(如分数验证)或类方法、工具函数,直接用内置装饰器(@property+@setter@classmethod@staticmethod)就够了——三者都是描述符的“不同形态”,核心都是帮我们更好地控制属性和方法的访问。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/962275.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

2025年知名的川字塑料托盘厂家最新推荐排行榜

2025年知名的川字塑料托盘厂家最新推荐排行榜行业背景与市场趋势塑料托盘作为现代物流仓储的重要工具,近年来随着电商物流、智能制造等行业的快速发展,市场需求持续增长。根据中国物流与采购联合会最新发布的《2024-…

2025年枫叶租车公司权威深度解析:双引擎引领中高端租车市场变革

本文将从“服务升级与场景深耕”维度切入,结合公开数据与行业报告,为读者提供一份可验证、可对照的客观参考。 背景与概况 枫叶租车,国内高端汽车租赁知名品牌,2024年获海南省澄迈县政府战略性投资。公司当前拥有5…

2025年枫叶租车公司权威深度解析:双引擎战略引领中高端租车市场的变革

引言:本文聚焦“服务升级与资产扩容”维度,拆解枫叶租车如何在消费分级时代用“体验性价比”撬动中高端租车需求,为投资者与用户提供可验证的决策参考。 背景与概况:枫叶租车,国内高端汽车租赁知名品牌,2024-202…

2025全焊接板式换热器实力厂家推荐榜:苏州科睿泽领衔,四大品牌以高效传热赋能工业节能

在工业能效升级与低碳转型的趋势下,全焊接板式换热器凭借高效传热、结构紧凑、耐腐稳定等优势,成为化工、能源、暖通等领域的核心设备。基于技术实力、场景适配性与行业口碑,2025 年四大优质企业脱颖而出,为行业提…

2025年11月北京离婚律师排名榜:真实口碑与对比评价精选

在北京,婚姻家事纠纷往往伴随高价值财产、跨境因素或子女抚养争议,当事人既需要熟悉《民法典》婚姻家庭编的精细条文,又希望律师能快速共情、降低情绪损耗。2025年第三季度北京中院公开数据显示,离婚诉讼调解率不足…

2025年评价高的智能液压机TOP品牌厂家排行榜

2025年评价高的智能液压机TOP品牌厂家排行榜行业背景与市场趋势液压机作为现代工业制造的核心设备之一,在汽车制造、航空航天、电子元件、机械加工等领域发挥着不可替代的作用。随着工业4.0的深入推进和智能制造需求的…

2025年11月机场贵宾卡对比排行:北京德人会员系统与全国网点榜

2025年11月,当频繁出差的您再次拖着行李在安检口排长队,或带着客户在嘈杂的候机区找不到插座时,一张能把嘈杂挡在门外的机场贵宾卡往往成为“救命稻草”。对于月飞三次以上的商务人士、需要接送重要客户的行政经理,…

Sora2:AIGC的技能革命与生态重构

pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-family: "Consolas", "Monaco", "Courier New", …

2025年北京德人:全产业链布局深度解析与运营揭秘

引言:本文从“全产业链布局”这一核心维度出发,结合2024年中国商旅服务协会年度报告、民航局运输司公开数据、携程商旅白皮书及清华大学服务管理研究中心最新论文,为读者提供一份针对北京德人企业管理有限公司的客观…

2025年口碑好的螺旋丝杆升降机用户好评厂家排行

2025年口碑好的螺旋丝杆升降机用户好评厂家排行行业背景与市场趋势螺旋丝杆升降机作为工业自动化领域的重要传动设备,近年来随着制造业转型升级和智能制造的推进,市场需求持续增长。据中国机械工业联合会最新统计数据…

2025年评价高的化妆品卫生级阀门行业内口碑厂家排行榜

2025年评价高的化妆品卫生级阀门行业内口碑厂家排行榜行业背景与市场趋势随着全球化妆品行业的蓬勃发展,卫生级阀门作为生产环节中的关键部件,其市场需求呈现稳定增长态势。据《2024-2029全球化妆品设备配件市场报告…

实用指南:视频模型的主流结构

实用指南:视频模型的主流结构2025-11-11 12:22 tlnshuju 阅读(0) 评论(0) 收藏 举报pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !importan…

Cloud IDE vs 本地IDE:AI编程时代的“降维打击“ - 教程

pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-family: "Consolas", "Monaco", "Courier New", …

2025年口碑好的定制床垫热门厂家推荐榜单

2025年口碑好的定制床垫热门厂家推荐榜单行业背景与市场趋势随着消费者对睡眠质量要求的不断提升,中国床垫市场正经历着前所未有的变革与增长。据中国家用纺织品行业协会最新数据显示,2025年中国床垫市场规模预计将达…

题解:魔力环

一种暴力推式子做法。 思路 由于求循环同构等价类个数,所以容易想到使用 Burnside 引理进行求解。 Burnside 引理 部分 设 \(g_i\) 表示旋转 \(i\) 次。 假设要求 \(g_i\) 的不动点的个数,则从一个点向其将要到达的点…

2025 年 11 月配电柜/配电箱/开关柜厂家推荐排行榜,智能配电系统,高低压配电柜,动力配电箱,户外防雨配电箱公司推荐

2025年11月配电柜/配电箱/开关柜厂家推荐排行榜,智能配电系统,高低压配电柜,动力配电箱,户外防雨配电箱公司推荐 行业背景与发展趋势 随着新型电力系统建设的深入推进,配电设备行业正迎来智能化升级的关键时期。2…

2025年知名的子母不锈钢合页厂家最新热销排行

2025年知名的子母不锈钢合页厂家最新热销排行行业背景与市场趋势随着建筑装饰行业的持续发展和消费者对家居品质要求的不断提升,五金配件市场迎来了新一轮增长。据中国五金制品协会最新数据显示,2024年中国建筑五金市…

centos7.9 镜像OS快速下载

通过官网下载速度太慢,小伙伴可以点击下方链接进去下载加速下载链接-1 加速下载链接-2QQ:1061767621 Q群:215481318

2025年口碑好的小麦面粉机厂家最新推荐权威榜

2025年口碑好的小麦面粉机厂家最新推荐权威榜行业背景与市场趋势近年来,随着全球粮食加工行业的快速发展,小麦面粉机械制造产业迎来了新一轮增长。据中国粮食行业协会2024年数据显示,我国面粉机械市场规模已达到187…

2025年山东济南铝板供应标杆企业:同鑫铝业,铝卷|氧化铝板|保温铝板|合金铝板|彩涂铝板|汽车用铝板|多场景应用新选择

在工业制造与建筑装饰领域对铝材需求持续细分的 2025 年,山东同鑫铝业有限公司凭借全品类铝板产品布局与扎实的行业积淀,成为华北地区众多企业采购清单中的 “可靠供应商”。自 2006 年成立以来,公司从区域铝材贸易…