脚本专家 发表于 2026-7-5 10:00:05

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

Python 内置装饰器通常分为两类:一类是语法内置、无需导入即可直接使用的原生核心装饰器,另一类来自官方标准库,导入对应模块即可使用。它们覆盖了面向对象封装、性能优化、代码简化、架构设计等场景。下面逐一讲解高频实用装饰器。

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

1. @property
将类的方法伪装成属性访问,用于实现受控的 getter/setter/deleter,是 Python 实现“封装”特性的首选方式。例如,在配置类中做参数校验:

class AgentConfig:
    def __init__(self, temperature: float):
      self._temperature = temperature

    @property
    def temperature(self):
      return self._temperature

    @temperature.setter
    def temperature(self, value: float):
      if not 0 <= value <= 2:
            raise ValueError("temperature 必须在 0~2 之间")
      self._temperature = value

config = AgentConfig(0.7)
print(config.temperature)# 像属性一样读取
config.temperature = 1.5   # 赋值时自动校验


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

3. @staticmethod
标记为静态方法,不接收 self 也不接收 cls,本质是放在类命名空间里的普通函数,适合存放与类相关但不需要实例上下文工具逻辑。例如:

class AgentConfig:
    @staticmethod
    def validate_temperature(value: float) -> bool:
      return 0 <= value <= 2

print(AgentConfig.validate_temperature(0.7))# True


二、标准库高频内置装饰器
(一)dataclasses 模块:@dataclass
Python 3.7+ 引入的类装饰器,自动为数据类生成 __init__、__repr__、__eq__ 等方法,大幅减少样板代码。适用于定义配置、消息体、搜索结果等数据结构。

from dataclasses import dataclass
from typing import Optional

@dataclass
class SearchResult:
    title: str
    url: str
    snippet: str
    score: Optional = None

result = SearchResult(title="华为最新手机", url="https://example.com", snippet="Mate 70 系列发布")
print(result)
# 输出:SearchResult(title='华为最新手机', url='https://example.com', snippet='Mate 70 系列发布', score=None)


(二)functools 模块:工具装饰器集合
1. @functools.wraps
写自定义装饰器时的标配,保留原函数的元信息(函数名、文档字符串、参数签名)。只要自己写装饰器就必须用它:

import time
from functools import wraps

def timer(func):
    @wraps(func)
    def wrapper(*args, **kwargs):
      start = time.time()
      result = func(*args, **kwargs)
      print(f"{func.__name__} 耗时: {time.time()-start:.2f}s")
      return result
    return wrapper

@timer
def search(query: str):
    """搜索函数"""
    time.sleep(0.1)
    return f"搜索结果: {query}"

print(search.__name__)# search(不加wraps会变成wrapper)
print(search.__doc__)   # 搜索函数


2. @functools.lru_cache / @functools.cache
函数结果缓存装饰器,自动记住输入-输出映射,相同输入直接返回缓存结果,避免重复计算或重复调用。@cache(Python 3.9+)等价于 @lru_cache(maxsize=None);@lru_cache 可设置最大缓存数量,淘汰最少使用的缓存。适用于缓存搜索结果、RAG 检索结果等:

from functools import lru_cache

@lru_cache(maxsize=100)
def search_web(query: str) -> str:
    print(f"执行真实搜索: {query}")
    return f"{query} 的搜索结果"

search_web("华为最新手机")# 执行真实搜索
search_web("华为最新手机")# 直接返回缓存,不打印第二次

注意:只能缓存参数可哈希的函数(参数为字符串、数字、元组等),列表、字典等不可哈希类型不能直接用。

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

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

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

(四)contextlib 模块:@contextmanager
将生成器函数转换为上下文管理器,替代手动编写 __enter__/__exit__,简化资源管理场景(如计时、临时目录):

from contextlib import contextmanager
import time

@contextmanager
def timer(name: str):
    start = time.time()
    yield
    print(f"{name} 总耗时: {time.time()-start:.2f}s")

with timer("搜索流程"):
    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 智能体、数据分析工具等工程化项目中尤为实用。

热心网友3 发表于 2026-7-5 10:10:00

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

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

热心网友3 发表于 2026-7-5 10:10:00

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

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

热心网友3 发表于 2026-7-5 10:10:00

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

感谢楼主的详细整理!平时写 Python 经常用 @property 和 @dataclass,确实省了不少样板代码。@lru_cache 的哈希限制提醒得很关键,之前用列表做参数直接报错才明白。想请教一下,@singledispatch 在项目里有没有比较经典的用例?比如处理 JSON 数据时按类型区分?
页: [1]
查看完整版本: Python内置装饰器详解:从@property到@dataclass与@lru_cache实战