跳到主要内容
极客日志极客日志面向AI+效率的开发者社区
首页博客GitHub 精选镜像AI 生图工具UI配色美学隐私政策关于联系
搜索内容 / 工具 / 仓库 / 镜像...⌘K搜索
注册
博客列表
Python

Python 描述符详解:属性访问拦截机制

Python 描述符(Descriptor)是一种实现属性访问拦截的代理类。通过实现 __get__、__set__、__delete__ 魔术方法,可以在读取、写入或删除属性时执行自定义逻辑,如参数校验、日志记录或动态计算。相比传统的 Getter/Setter,描述符能保持 API 的自然性并提高代码复用率。Python 内置的 @property、方法绑定及 ORM 框架均基于此机制。

念念不忘发布于 2026/3/26更新于 2026/7/2359 浏览

在 Python 里,通常我们访问对象的属性(比如 obj.x),就像是从货架上直接拿东西,没有任何阻拦。

但是,如果你想在拿东西(读取)或放东西(写入)的时候搞点'小动作'——比如参数检查、日志记录、或者动态计算——你就需要一个'看门大爷'。

这个'看门大爷',就是描述符 (Descriptor)。

一、核心概念:什么是描述符?

在 Python 对象模型里,描述符就是一个把'属性访问'变成'方法调用'的代理类。

只要一个类实现了以下任意一个魔术方法,它生成的对象就是描述符:

  1. __get__:有人要读属性时触发(对应 val = obj.x)。
  2. __set__:有人要改属性时触发(对应 obj.x = val)。
  3. __delete__:有人要删属性时触发(对应 del obj.x)。
形象比喻
  • 普通属性:像公共储物柜。你也拿,我也拿,谁都能塞进去任何东西(甚至塞个垃圾数据)。
  • 描述符:像银行柜台。
    • 你想存钱?(__set__):柜员会检查钞票真伪(数据校验)。
    • 你想取钱?(__get__):柜员会核对你的身份,甚至计算利息给你(动态计算)。

二、三大金刚详解(怎么用?)

我们通过写一个**'强类型检查器'来演示。假设你需要定义一个学生类,要求分数必须是 0-100 的整数**。

1. 定义描述符类(制定规则)

这是'看门大爷'的自我修养。

class ScoreDescriptor:
    def __init__(self, subject_name):
        self.subject_name = subject_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} 成绩...")
         instance  :
             
        
         (instance, .internal_name, )

    
     ():
        ()
        
          (value, ):
             TypeError()
          ( <= value <= ):
             ValueError()
        
        (instance, .internal_name, value)

    
     ():
        ()
        (instance, .internal_name)
if
is
None
return
self
# 从 instance 的字典里把真值拿出来
return
getattr
self
0
# 【写】当有人执行 student.math = 90 时
def
__set__
self, instance, value
print
f"👮 [SET] 正在批改 {self.subject_name},分数为 {value}"
# --- 核心逻辑:拦截并检查 ---
if
not
isinstance
int
raise
f"{self.subject_name} 分数必须是整数!"
if
not
0
100
raise
f"{self.subject_name} 分数必须在 0-100 之间!"
# 检查通过,存入 instance 的字典
setattr
self
# 【删】当有人执行 del student.math 时
def
__delete__
self, instance
print
f"🗑️ [DEL] 删除 {self.subject_name} 成绩"
delattr
self
2. 使用描述符(聘请看门大爷)

注意:描述符必须定义在类属性(Class Attribute)层级。

class Student:
    # 聘请两个'看门大爷'分别管理 数学 和 英语
    math = ScoreDescriptor("math")
    english = ScoreDescriptor("english")

    def __init__(self, name, math_score, english_score):
        self.name = name
        # 这里看似是普通赋值,实际上触发了 math.__set__()
        self.math = math_score
        self.english = english_score

    def __repr__(self):
        return f"Student(name={self.name}, math={self.math}, english={self.english})"
3. 运行效果(见证奇迹)
s1 = Student("小明", 85, 92)
# 输出:
# 👮 [SET] 正在批改 math,分数为 85
# 👮 [SET] 正在批改 english,分数为 92

# --- 测试读取 ---
print(s1.math)
# 输出:
# 🕵️ [GET] 正在查看 math 成绩...
# 85

# --- 测试非法赋值 ---
try:
    s1.math = 120  # 超过 100 分
except ValueError as e:
    print(f"\n❌ 报错啦:{e}")
# 输出:❌ 报错啦:math 分数必须在 0-100 之间!

try:
    s1.english = "A+"  # 类型错误
except TypeError as e:
    print(f"❌ 报错啦:{e}")
# 输出:❌ 报错啦:english 分数必须是整数!

三、为什么要用描述符?(语言对比视角)

你可能会问:'我在 Student 类里写个 get_math 和 set_math 不行吗?'

当然行,但描述符有两个巨大的优势:

  1. 保持 API 优雅: 使用者依然可以用 s.math = 90 这种自然的语法,而不是 s.set_math(90)。这在 Python 这种动态语言里非常重要。
  2. 代码复用(DRY 原则): 如果你有 10 个学科(Math, English, Physics…),用传统 Getter/Setter 你要写 10 遍检查逻辑(if value < 0…)。 用描述符,你写一次 ScoreDescriptor 类,然后实例化 10 次就行了。 这就好比:你不需要给每个房间都专门雇一个保安,你只需要雇一个保安公司(描述符类),然后给每个房间派个保安(实例)就行。

四、你其实天天在用它

即使你没写过 __get__,你也一直在用描述符:

  1. @property: 这是一个内置的描述符,它是把一个函数伪装成属性的最简单方式。
  2. 方法 (Methods): 为什么 def foo(self): 定义在类里,通过实例 obj.foo() 调用时 self 就会自动传进去? 因为 函数 (Function) 本身就是一个实现了 __get__ 的描述符!每次访问 obj.foo,__get__ 都会动态生成一个'绑定方法 (Bound Method)'对象返回给你。
  3. Django / SQLAlchemy: name = models.CharField(max_length=100) —— 这里的 CharField 就是个巨大的描述符,它负责把 Python 对象的数据转换成 SQL 语句。

五、总结

  • 描述符 = 属性访问拦截器。
  • __get__ = 拦截读取,控制返回什么值。
  • __set__ = 拦截赋值,控制能否写入、写入什么。
  • instance 参数 = 谁在调用我?(具体的对象实例)。
  • owner 参数 = 谁拥有我?(类本身)。

目录

  1. 一、核心概念:什么是描述符?
  2. 形象比喻
  3. 二、三大金刚详解(怎么用?)
  4. 1. 定义描述符类(制定规则)
  5. 2. 使用描述符(聘请看门大爷)
  6. 3. 运行效果(见证奇迹)
  7. 输出:
  8. 👮 [SET] 正在批改 math,分数为 85
  9. 👮 [SET] 正在批改 english,分数为 92
  10. --- 测试读取 ---
  11. 输出:
  12. 🕵️ [GET] 正在查看 math 成绩...
  13. 85
  14. --- 测试非法赋值 ---
  15. 输出:❌ 报错啦:math 分数必须在 0-100 之间!
  16. 输出:❌ 报错啦:english 分数必须是整数!
  17. 三、为什么要用描述符?(语言对比视角)
  18. 四、你其实天天在用它
  19. 五、总结
  • 免费图片AI生成工具免费生成了解详情
  • Magick API 一键接入全球大模型注册送1000万token查看
  • 免费图片视频在线生成30秒,将你的创意变成现实开始设计
  • X/Twitter免费视频下载器免登陆无限额度免费视频解析下载了解详情
  • 100+免费在线小游戏爽一把
极客日志微信公众号二维码

微信扫一扫,关注极客日志

微信公众号「极客日志V2」,在微信中扫描左侧二维码关注。展示文案:极客日志V2 zeeklog

更多推荐文章

查看全部
  • Android WebRTC SDK 入门指南:从零搭建实时音视频通信应用
  • Linux 编程中常用的控制线程函数详解
  • Prompt 工程通用框架及案例:模型智能决定上限
  • 机器人 DH 参数模型与正运动学
  • RocketMQ 5.0 延时消息架构:时间轮算法与分布式调度实践
  • Flutter eth_sig_util 鸿蒙适配指南:以太坊加密签名与 Web3 开发
  • RPA 机器人流程自动化技术解析与应用
  • Python 编程快速入门指南
  • AI 幻觉深度解析:成因、风险与应对策略
  • 程序员阅兵
  • LLM 大模型产品经理学习指南:从基础理论到实战应用
  • 2025 年 12 月 GitHub 十大热门开源项目
  • 基于 Playwright 封装 Web 爬虫并隐藏自动化特征
  • ChatLaw 袁粒:法律大模型如何助力个人维权与行业思考
  • 深入理解 Git 核心概念与主流工作流实践
  • 利用内网穿透技术实现 OpenClaw 远程访问与部署
  • 2026 年 AI 辅助编程工具对比:Copilot、Cursor、Claude Code 与 Codex 解析
  • 网络安全入门核心知识体系与学习路径指南
  • 利用 AI 大模型与 TradingView 构建自动交易策略
  • 数据结构:基于队列的二叉树深度计算(层次遍历实现)

相关免费在线工具

  • curl 转代码

    解析常见 curl 参数并生成 fetch、axios、PHP curl 或 Python requests 示例代码。 在线工具,curl 转代码在线工具,online

  • Base64 字符串编码/解码

    将字符串编码和解码为其 Base64 格式表示形式即可。 在线工具,Base64 字符串编码/解码在线工具,online

  • Base64 文件转换器

    将字符串、文件或图像转换为其 Base64 表示形式。 在线工具,Base64 文件转换器在线工具,online

  • Markdown转HTML

    将 Markdown(GFM)转为 HTML 片段,浏览器内 marked 解析;与 HTML转Markdown 互为补充。 在线工具,Markdown转HTML在线工具,online

  • HTML转Markdown

    将 HTML 片段转为 GitHub Flavored Markdown,支持标题、列表、链接、代码块与表格等;浏览器内处理,可链接预填。 在线工具,HTML转Markdown在线工具,online

  • JSON 压缩

    通过删除不必要的空白来缩小和压缩JSON。 在线工具,JSON 压缩在线工具,online