惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

cs.CV updates on arXiv.org
cs.CV updates on arXiv.org
Hacker News - Newest:
Hacker News - Newest: "LLM"
N
News and Events Feed by Topic
PCI Perspectives
PCI Perspectives
Help Net Security
Help Net Security
C
CXSECURITY Database RSS Feed - CXSecurity.com
V2EX - 技术
V2EX - 技术
Google Online Security Blog
Google Online Security Blog
Hacker News: Ask HN
Hacker News: Ask HN
cs.AI updates on arXiv.org
cs.AI updates on arXiv.org
P
Palo Alto Networks Blog
The Last Watchdog
The Last Watchdog
C
Cyber Attacks, Cyber Crime and Cyber Security
Security Latest
Security Latest
Application and Cybersecurity Blog
Application and Cybersecurity Blog
W
WeLiveSecurity
Cloudbric
Cloudbric
O
OpenAI News
CTFtime.org: upcoming CTF events
CTFtime.org: upcoming CTF events
Webroot Blog
Webroot Blog
www.infosecurity-magazine.com
www.infosecurity-magazine.com
S
Schneier on Security
罗磊的独立博客
雷峰网
雷峰网
S
Security Affairs
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
L
LINUX DO - 热门话题
NISL@THU
NISL@THU
量子位
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More
博客园 - 【当耐特】
博客园 - Franky
S
Security @ Cisco Blogs
Project Zero
Project Zero
AI
AI
T
Troy Hunt's Blog
Latest news
Latest news
Simon Willison's Weblog
Simon Willison's Weblog
S
SegmentFault 最新的问题
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
T
Tailwind CSS Blog
The Cloudflare Blog
K
KPMG report finds enterprise disconnect between AI and its ROI | CIO
Scott Helme
Scott Helme
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
Jina AI
Jina AI
I
Intezer
V
Visual Studio Blog
Cyberwarzone
Cyberwarzone
博客园 - 叶小钗

博客园 - 箫笛

Tkinter - Button 组件 Tkinter - Label 组件 Tkinter - tk 变量 Tkinter - 事件与绑定 Tkinter - 几何管理器 Tkinter - 核心概念 Tkinter - 快速开始 Tkinter - 介绍 Python 编程 - 条件表达式 Python 编程 - 星号下划线参数释义 shell 编程 - shell 脚本的交互方式 Python insall - macOS 系统安装python的几种方式 Miniconda - Python 环境管理工具 Tkinter - Python GUI 开发 Python 编程 - 下划线命名的区别 Python 编程 - 元类编程 Python 编程 - 多重继承与MRO Python 编程 - 类型注解 Python 编程 - 生成器表达式 Python 编程 - 集合 Python 编程 - 装饰器 Python 编程 - 闭包 Python 编程 - 列表推导式 Python 编程 - 函数式编程 Python 编程 - lambda 函数 Python 编程 - 文件操作 Python 编程 - 输入与输出 Python 编程 - 面向对象编程 Python 编程 - 元组(tuple) Python 编程 - 字符串(str) Python 编程 - 字典(dict) Python 编程 - 列表(list) Python 编程 - 数据类型和数据结构 Python 编程 - 语句 Python 编程 - 函数 windows - WSL 的安装与使用 shell编程 - dialog 程序使用指南 FE Team - 如何做好前端代码审查 git 提交的撤销和恢复 React15 - redux-saga 如何在saga中实现轮询接口调用? React15 - React CSS Modules BEM命名实践 React15 - React 15 中 componentWillReceiveProps 为什么会多次调用, 同时componentDidUpdate 也会多次调用? React15 - React15类组件多次执行render方法的原因? React15 - React15应用中代码逻辑复用方案 React15 - React状态同步问题解决 React15 - React 15 中 React.pureComponent 的使用场景 React15 - React 15应用在页面渲染时会多次执行类组件的render 函数的原因 React15 - React 15 中能用 componetDidUpdate 代替 componentWillReceiveProps 吗? React15 - React 15 生命周期函数详解 React15 - 如何在React 15中实现自定义的事件订阅与发送(例如组件间通信) React15 - React15应用中的事件订阅和发送机制 React15 - CSS中的BEM规范 React15 - React CSS Modules BEM命名实践 React15 - 写sass 样式文件,嵌套的结构好,还是扁平的结构好? React15 - sass 中 @mixin 和 @extend 的区别是什么? React15 - React 15 应用 如何使用Css moudules 方式进行模块化开发 React15 - React15应用Sass使用指南 React15 - React 15 应用如何进行性能优化?
Python 编程 - 描述符协议
箫笛 · 2026-06-30 · via 博客园 - 箫笛

描述符协议(Descriptor Protocol)是 Python 中一个核心且强大的底层机制。它允许你自定义对象属性的访问、赋值和删除行为。

简单来说,描述符就是实现了特定协议方法的 Python 对象,它作为一种“可重用的属性逻辑”存在,可以让你优雅地管理属性。

我们日常使用的许多 Python 高级特性,如 @property@classmethod@staticmethod,甚至是函数绑定机制,其底层都是由描述符协议实现的。

📜 描述符协议的核心方法

一个对象要成为描述符,需要实现以下一个或多个方法:

方法 签名 触发时机
__get__ __get__(self, instance, owner) 读取属性时 (如 obj.attr)
__set__ __set__(self, instance, value) 赋值属性时 (如 obj.attr = value)
__delete__ __delete__(self, instance) 删除属性时 (如 del obj.attr)
__set_name__ __set_name__(self, owner, name) (Python 3.6+) 类创建时自动调用,告知描述符其被赋值的属性名

这些方法的参数含义如下:

  • self: 描述符实例本身。
  • instance: 拥有该属性的实例对象(通过类访问时为 None)。
  • owner: 拥有该属性的
  • value: 要赋的新值。

重要前提:描述符协议只有在描述符对象被作为类属性(即定义在类体中)时才会生效。如果将其赋值给某个实例,则它只是一个普通对象,协议方法不会被触发。

⚖️ 数据描述符 vs. 非数据描述符

根据实现的方法,描述符分为两类,它们在属性查找时的优先级不同:

  1. 数据描述符 (Data Descriptor):同时实现了 __get____set__ 方法。它的优先级最高,会覆盖实例属性(instance.__dict__)。
  2. 非数据描述符 (Non-Data Descriptor):只实现了 __get__ 方法。它的优先级较低,只有在实例属性(instance.__dict__)中找不到对应属性时才会被触发。

这个优先级区别是理解 Python 属性查找机制的关键。

🔍 属性查找的完整链条

当你访问一个对象的属性(如 obj.attr)时,Python 遵循以下查找顺序:

  1. 查找数据描述符:在 obj 的类及其父类中查找 attr。如果它是一个数据描述符,则直接调用其 __get__ 方法并返回,查找结束
  2. 查找实例属性:检查 obj.__dict__ 字典。如果找到 attr,则直接返回其值,查找结束
  3. 查找非数据描述符:在 obj 的类及其父类中查找 attr。如果它是一个非数据描述符,则调用其 __get__ 方法并返回。
  4. 查找普通类属性:如果仍然没找到,则返回类中定义的普通属性。
  5. 触发 __getattr__:如果以上都未找到,且类中定义了 __getattr__ 方法,则调用它。
  6. 抛出 AttributeError:最终仍未找到,则抛出 AttributeError

💡 典型应用场景

描述符在 Python 生态中被广泛应用:

  • 数据验证与类型检查:在赋值时自动验证数据的合法性。
  • 惰性计算 (Lazy Evaluation):属性只在首次访问时进行计算,节省资源。
  • ORM(对象关系映射):如 Django 的 models.Field 和 SQLAlchemy 的列类型,都是描述符。
  • 实现 @property:将方法调用变成属性访问。
  • 实现 @classmethod@staticmethod:改变方法的调用方式。
  • 缓存与日志记录:在属性访问时添加额外的缓存或日志逻辑。

💻 实战示例:实现带类型检查的属性

下面这个例子展示了如何使用描述符创建一个支持类型检查的属性:

class TypedProperty:
    def __init__(self, expected_type):
        self.expected_type = expected_type

    def __set_name__(self, owner, name):
        # 获取属性在类中的名字,例如 'name' 或 'age'
        self.name = name

    def __get__(self, instance, owner):
        if instance is None:
            return self
        # 从实例的 __dict__ 中获取值
        return instance.__dict__.get(self.name)

    def __set__(self, instance, value):
        # 在赋值时进行类型检查
        if not isinstance(value, self.expected_type):
            raise TypeError(f"Expected {self.expected_type.__name__}, got {type(value).__name__}")
        # 验证通过,将值存入实例的 __dict__
        instance.__dict__[self.name] = value

class Person:
    # 将描述符作为类属性使用
    name = TypedProperty(str)
    age = TypedProperty(int)

    def __init__(self, name, age):
        self.name = name
        self.age = age

现在,Person 类的属性拥有了类型检查的能力:

>>> p = Person("Alice", 30)
>>> p.name
'Alice'
>>> p.age
30

>>> p.name = 123  # 尝试赋一个非字符串值
Traceback (most recent call last):
  ...
TypeError: Expected str, got int

在这个例子中,TypedProperty 类实现了描述符协议。__set_name__ 让它知道自己在 Person 类中的属性名,__get____set__ 则接管了对该属性的读写操作,并在写入时加入了类型校验逻辑。

描述符协议是理解 Python 底层运作和进行高级编程的利器,它让你能够以优雅、可复用的方式控制属性行为。