查看: 178|回复: 3

Python内置装饰器详解:从@property到@dataclass与@lru_cache实战

[复制链接]
发表于 3 小时前 | 显示全部楼层 |阅读模式
Python 内置装饰器通常分为两类:一类是语法内置、无需导入即可直接使用的原生核心装饰器,另一类来自官方标准库,导入对应模块即可使用。它们覆盖了面向对象封装、性能优化、代码简化、架构设计等场景。下面逐一讲解高频实用装饰器。

一、原生核心装饰器(无需导入)
这三个装饰器作用于类的方法,无需 import 直接使用。

1. @property
将类的方法伪装成属性访问,用于实现受控的 getter/setter/deleter,是 Python 实现“封装”特性的首选方式。例如,在配置类中做参数校验:
  1. class AgentConfig:
  2.     def __init__(self, temperature: float):
  3.         self._temperature = temperature
  4.     @property
  5.     def temperature(self):
  6.         return self._temperature
  7.     @temperature.setter
  8.     def temperature(self, value: float):
  9.         if not 0 <= value <= 2:
  10.             raise ValueError("temperature 必须在 0~2 之间")
  11.         self._temperature = value
  12. config = AgentConfig(0.7)
  13. print(config.temperature)  # 像属性一样读取
  14. config.temperature = 1.5   # 赋值时自动校验
复制代码

2. @classmethod
标记为类方法,第一个参数固定为 cls,无需实例化即可通过类名调用。常用于实现工厂模式(如 from_env 构造)。

3. @staticmethod
标记为静态方法,不接收 self 也不接收 cls,本质是放在类命名空间里的普通函数,适合存放与类相关但不需要实例上下文工具逻辑。例如:
  1. class AgentConfig:
  2.     @staticmethod
  3.     def validate_temperature(value: float) -> bool:
  4.         return 0 <= value <= 2
  5. print(AgentConfig.validate_temperature(0.7))  # True
复制代码

二、标准库高频内置装饰器
(一)dataclasses 模块:@dataclass
Python 3.7+ 引入的类装饰器,自动为数据类生成 __init__、__repr__、__eq__ 等方法,大幅减少样板代码。适用于定义配置、消息体、搜索结果等数据结构。
  1. from dataclasses import dataclass
  2. from typing import Optional
  3. @dataclass
  4. class SearchResult:
  5.     title: str
  6.     url: str
  7.     snippet: str
  8.     score: Optional[float] = None
  9. result = SearchResult(title="华为最新手机", url="https://example.com", snippet="Mate 70 系列发布")
  10. print(result)
  11. # 输出:SearchResult(title='华为最新手机', url='https://example.com', snippet='Mate 70 系列发布', score=None)
复制代码

(二)functools 模块:工具装饰器集合
1. @functools.wraps
写自定义装饰器时的标配,保留原函数的元信息(函数名、文档字符串、参数签名)。只要自己写装饰器就必须用它:
  1. import time
  2. from functools import wraps
  3. def timer(func):
  4.     @wraps(func)
  5.     def wrapper(*args, **kwargs):
  6.         start = time.time()
  7.         result = func(*args, **kwargs)
  8.         print(f"{func.__name__} 耗时: {time.time()-start:.2f}s")
  9.         return result
  10.     return wrapper
  11. @timer
  12. def search(query: str):
  13.     """搜索函数"""
  14.     time.sleep(0.1)
  15.     return f"搜索结果: {query}"
  16. print(search.__name__)  # search(不加wraps会变成wrapper)
  17. print(search.__doc__)   # 搜索函数
复制代码

2. @functools.lru_cache / @functools.cache
函数结果缓存装饰器,自动记住输入-输出映射,相同输入直接返回缓存结果,避免重复计算或重复调用。@cache(Python 3.9+)等价于 @lru_cache(maxsize=None);@lru_cache 可设置最大缓存数量,淘汰最少使用的缓存。适用于缓存搜索结果、RAG 检索结果等:
  1. from functools import lru_cache
  2. @lru_cache(maxsize=100)
  3. def search_web(query: str) -> str:
  4.     print(f"执行真实搜索: {query}")
  5.     return f"{query} 的搜索结果"
  6. search_web("华为最新手机")  # 执行真实搜索
  7. search_web("华为最新手机")  # 直接返回缓存,不打印第二次
复制代码
注意:只能缓存参数可哈希的函数(参数为字符串、数字、元组等),列表、字典等不可哈希类型不能直接用。

3. @functools.total_ordering
类装饰器,只需实现 __eq__ 和一个比较方法(如 __lt__),自动补全 <=、>、>= 等全部比较运算符,常用于定义排序逻辑。

4. @functools.singledispatch
实现单分派泛函数——根据第一个参数的类型自动分发到不同处理逻辑,相当于按类型重载,适合统一入口但不同类型处理不同的函数。

(三)abc 模块:@abstractmethod
定义抽象基类的抽象方法,强制所有子类必须实现该方法,否则无法实例化。是接口规范和基类模板的核心工具。

(四)contextlib 模块:@contextmanager
将生成器函数转换为上下文管理器,替代手动编写 __enter__/__exit__,简化资源管理场景(如计时、临时目录):
  1. from contextlib import contextmanager
  2. import time
  3. @contextmanager
  4. def timer(name: str):
  5.     start = time.time()
  6.     yield
  7.     print(f"{name} 总耗时: {time.time()-start:.2f}s")
  8. with timer("搜索流程"):
  9.     time.sleep(0.2)
复制代码

三、其他常用标准库装饰器
- @enum.unique(enum 模块):约束枚举类取值唯一,避免重复。
- @typing.overload(typing 模块):为函数定义多组类型重载注解,仅用于静态类型检查,运行时无实际效果。
- @asyncio.coroutine(asyncio 模块):旧版异步协程装饰器,已被 async/await 取代,仅兼容老代码。

四、项目高频总结
按使用频率排序:
数据结构:@dataclass(定义消息、配置、结果)
类设计:@property、@classmethod、@abstractmethod(基类与封装)
性能优化:@lru_cache(缓存重复调用)
自定义工具:@functools.wraps(写自己的装饰器)
资源管理:@contextmanager(上下文逻辑)
掌握这些内置装饰器能显著提升代码质量与开发效率,特别在构建 AI 智能体、数据分析工具等工程化项目中尤为实用。
回复

使用道具 举报

发表于 2 小时前 | 显示全部楼层

Re: Python内置装饰器详解:从@property到@dataclass与@lru_cache实战

楼主总结得非常全面,把 Python 内置装饰器的分类和使用场景讲得很清楚。尤其是 @property 结合 setter 做参数校验、@dataclass 减少样板代码、@lru_cache 用来缓存高频调用这几个例子,都是日常开发中非常实用的。我之前写自定义装饰器经常忘记加 @wraps,结果调试时看到函数名变成 wrapper 就很懵,现在算是记住了。另外想请教一下,如果缓存的对象需要根据时间失效(比如搜索结果过一段时间就过期),除了手动清缓存或设置 expiry,有没有更优雅的内置装饰器用法?
回复 支持 反对

使用道具 举报

发表于 2 小时前 | 显示全部楼层

Re: Python内置装饰器详解:从@property到@dataclass与@lru_cache实战

看了楼主的分享,受益匪浅!一直知道这些装饰器但没系统梳理过,现在清晰多了。尤其 @lru_cache 用于缓存查询结果这个思路很实用,之前自己在写爬虫时重复请求同一个 URL 总是要手动加字典缓存,原来内置装饰器直接搞定。另外 @total_ordering 也很节省模板代码,之前实现排序类老要手写所有比较方法。感谢楼主,已收藏!
回复 支持 反对

使用道具 举报

发表于 2 小时前 | 显示全部楼层

Re: Python内置装饰器详解:从@property到@dataclass与@lru_cache实战

感谢楼主的详细整理!平时写 Python 经常用 @property 和 @dataclass,确实省了不少样板代码。@lru_cache 的哈希限制提醒得很关键,之前用列表做参数直接报错才明白。想请教一下,@singledispatch 在项目里有没有比较经典的用例?比如处理 JSON 数据时按类型区分?
回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 注册

本版积分规则

指导单位

江苏省公安厅

江苏省通信管理局

浙江省台州刑侦支队

DEFCON GROUP 86025

Hacking Group 021A

旗下站点

态势感知中心

应急响应中心

红盟安全

联系我们

官方QQ群:112851260

官方邮箱:security#ihonker.org(#改成@)

官方核心成员

关注微信公众号

Archiver|手机版|小黑屋| ( 沪ICP备2021026908号 )

GMT+8, 2026-7-5 13:03 , Processed in 0.034367 second(s), 18 queries , Gzip On, Redis On.

Powered by ihonker.com

Copyright © 2015-现在.

  • 返回顶部