2026/2/20 12:20:57
网站建设
项目流程
怎么做网页代码,北京网站优化诊断,最新网游排行榜2024,兴仁企业建站公司在 Python 里#xff0c;通常我们访问对象的属性#xff08;比如 obj.x#xff09;#xff0c;就像是从货架上直接拿东西#xff0c;没有任何阻拦。
但是#xff0c;如果你想在拿东西#xff08;读取#xff09;或放东西#xff08;写入#xff09;的时候搞点“小动作…在 Python 里通常我们访问对象的属性比如obj.x就像是从货架上直接拿东西没有任何阻拦。但是如果你想在拿东西读取或放东西写入的时候搞点“小动作”——比如参数检查、日志记录、或者动态计算——你就需要一个“看门大爷”。这个“看门大爷”就是描述符 (Descriptor)。一、 核心概念什么是描述符在 Python 对象模型里描述符就是一个把“属性访问”变成“方法调用”的代理类。只要一个类实现了以下任意一个魔术方法它生成的对象就是描述符__get__有人要读属性时触发对应val obj.x。__set__有人要改属性时触发对应obj.x val。__delete__有人要删属性时触发对应del obj.x。形象比喻普通属性像公共储物柜。你也拿我也拿谁都能塞进去任何东西甚至塞个垃圾数据。描述符像银行柜台。你想存钱__set__柜员会检查钞票真伪数据校验。你想取钱__get__柜员会核对你的身份甚至计算利息给你动态计算。二、 三大金刚详解怎么用我们通过写一个**“强类型检查器”来演示。假设你需要定义一个学生类要求分数必须是 0-100 的整数**。1. 定义描述符类制定规则这是“看门大爷”的自我修养。classScoreDescriptor:def__init__(self,subject_name):self.subject_namesubject_name# 我们用一个私有变量名来存真正的数据防止死循环self.internal_name_subject_name# 【读】当有人访问 student.math 时def__get__(self,instance,owner):# instance: 就是那个 student 对象 (如果是 Student.math 访问这里是 None)# owner: 就是 Student 类本身print(f️ [GET] 正在查看{self.subject_name}成绩...)ifinstanceisNone:returnself# 从 instance 的字典里把真值拿出来returngetattr(instance,self.internal_name,0)# 【写】当有人执行 student.math 90 时def__set__(self,instance,value):print(f [SET] 正在批改{self.subject_name}分数为{value})# --- 核心逻辑拦截并检查 ---ifnotisinstance(value,int):raiseTypeError(f{self.subject_name}分数必须是整数)ifnot(0value100):raiseValueError(f{self.subject_name}分数必须在 0-100 之间)# 检查通过存入 instance 的字典setattr(instance,self.internal_name,value)# 【删】当有人执行 del student.math 时def__delete__(self,instance):print(f️ [DEL] 删除{self.subject_name}成绩)delattr(instance,self.internal_name)2. 使用描述符聘请看门大爷注意描述符必须定义在类属性Class Attribute层级。classStudent:# 聘请两个“看门大爷”分别管理 数学 和 英语mathScoreDescriptor(math)englishScoreDescriptor(english)def__init__(self,name,math_score,english_score):self.namename# 这里看似是普通赋值实际上触发了 math.__set__()self.mathmath_score self.englishenglish_scoredef__repr__(self):returnfStudent(name{self.name}, math{self.math}, english{self.english})3. 运行效果见证奇迹s1Student(小明,85,92)# 输出:# [SET] 正在批改 math分数为 85# [SET] 正在批改 english分数为 92# --- 测试读取 ---print(s1.math)# 输出:# ️ [GET] 正在查看 math 成绩...# 85# --- 测试非法赋值 ---try:s1.math120# 超过 100 分exceptValueErrorase:print(f\n❌ 报错啦:{e})# 输出: ❌ 报错啦: math 分数必须在 0-100 之间try:s1.englishA# 类型错误exceptTypeErrorase:print(f❌ 报错啦:{e})# 输出: ❌ 报错啦: english 分数必须是整数三、 为什么要用描述符C 视角你可能会问“我在Student类里写个get_math和set_math不行吗”当然行但描述符有两个巨大的优势保持 API 优雅使用者依然可以用s.math 90这种自然的语法而不是s.set_math(90)。这在 Python 这种动态语言里非常重要。代码复用DRY 原则如果你有 10 个学科Math, English, Physics…用传统 Getter/Setter 你要写 10 遍检查逻辑if value 0…。用描述符你写一次ScoreDescriptor类然后实例化 10 次就行了。这就好比你不需要给每个房间都专门雇一个保安你只需要雇一个保安公司描述符类然后给每个房间派个保安实例就行。四、 你其实天天在用它即使你没写过__get__你也一直在用描述符property这是一个内置的描述符它是把一个函数伪装成属性的最简单方式。方法 (Methods)为什么def foo(self):定义在类里通过实例obj.foo()调用时self就会自动传进去因为函数(Function)本身就是一个实现了__get__的描述符每次访问obj.foo__get__都会动态生成一个“绑定方法(Bound Method)”对象返回给你。Django / SQLAlchemyname models.CharField(max_length100)—— 这里的CharField就是个巨大的描述符它负责把 Python 对象的数据转换成 SQL 语句。五、 总结描述符属性访问拦截器。__get__ 拦截读取控制返回什么值。__set__ 拦截赋值控制能否写入、写入什么。instance参数 谁在调用我具体的对象实例。owner参数 谁拥有我类本身。