python中装饰器是什么

  1. 装饰器是什么?

一句话:装饰器就是接收一个可调用对象并返回一个可调用对象的函数,用来在不改动原函数源码的前提下,为其增加/修改行为。

语法糖:

@decorator      # 等价于  func = decorator(func)
def func(...):
    ...

多个装饰器:

@A
@B
def f(...): pass
# 等价于 f = A(B(f))   ← 先 B 后 A

装饰器在函数定义时(导入时)就会执行,不是调用时。

  1. 最小可用装饰器(同步)
import time
from functools import wraps

def timing(func):
    @wraps(func)                      # 关键:保留原函数 __name__/__doc__/签名 等元数据
    def wrapper(*args, **kwargs):     # 透传所有参数
        t0 = time.perf_counter()
        try:
            return func(*args, **kwargs)
        finally:
            print(f"{func.__name__} took {time.perf_counter()-t0:.3f}s")
    return wrapper

@timing
def work(n): sum(range(n))

work(1_000_000)

务必使用 functools.wraps,否则调试、文档、类型检查都会变差。

  1. 带参数的装饰器(“三层结构”)

当我们写 @retry(times=3) 这种带配置的装饰器时,本质是装饰器工厂:

from functools import wraps
import time

def retry(times=3, exceptions=(Exception,), backoff=0.0):
    def deco(func):
        @wraps(func)
        def wrapper(*args, **kwargs):
            last = None
            for i in range(times):
                try:
                    return func(*args, **kwargs)
                except exceptions as e:
                    last = e
                    if backoff: time.sleep(backoff*(2**i))
            raise last
        return wrapper
    return deco

@retry(times=5, backoff=0.1)
def may_fail(): ...

三层分别是:工厂 → 真正装饰器 → wrapper。

  1. 装饰 async 协程函数

包装协程要用 async def 并 await 原函数:

from functools import wraps
import time, asyncio

def log_async(func):
    @wraps(func)
    async def wrapper(*args, **kwargs):
        t0 = time.perf_counter()
        try:
            return await func(*args, **kwargs)
        finally:
            print(f"{func.__name__} took {time.perf_counter()-t0:.3f}s")
    return wrapper

@log_async
async def aio():
    await asyncio.sleep(0.1)

  1. 类装饰器(给“类”加料)

装饰器也能接收并返回类对象:

def add_repr(cls):
    def __repr__(self):
        fields = ", ".join(f"{k}={v!r}" for k, v in self.__dict__.items())
        return f"{cls.__name__}({fields})"
    cls.__repr__ = __repr__
    return cls

@add_repr
class User:
    def __init__(self, name, age): self.name, self.age = name, age

标准库里常见的“类装饰器”:@dataclass, @total_ordering, @enum.unique …

  1. 类实现的装饰器(状态更好维护)

当装饰器需要可变状态(例如调用计数、限流)时,用类更清晰:

from functools import wraps

class count_calls:
    def __init__(self, func):
        wraps(func)(self)
        self.func = func
        self.n = 0
    def __call__(self, *args, **kwargs):
        self.n += 1
        return self.func(*args, **kwargs)

@count_calls
def foo(): pass

foo(); foo()
# foo.n == 2

  1. 方法相关装饰器:@property / @staticmethod / @classmethod

它们本质是描述符/装饰器:

class A:
    def __init__(self, x): self._x = x

    @property              # 像属性一样访问,但有函数逻辑
    def x(self): return self._x

    @staticmethod          # 不接收 self
    def ping(): return "pong"

    @classmethod           # 接收 cls
    def make(cls): return cls(0)

  1. 类型提示友好写法(保留参数与返回类型)

用 ParamSpec + TypeVar,让装饰器不丢签名类型:

from typing import Callable, ParamSpec, TypeVar
from functools import wraps
P = ParamSpec("P")
R = TypeVar("R")

def timing_typed(func: Callable[P, R]) -> Callable[P, R]:
    @wraps(func)
    def wrapper(*args: P.args, **kwargs: P.kwargs) -> R:
        return func(*args, **kwargs)
    return wrapper

  1. 常见实战装饰器清单
    • 性能/可观测:计时、统计 QPS、打点上报
    • 健壮性:retry、超时、熔断、限流、缓存(functools.lru_cache)
    • 跨领域关注点:鉴权/权限、审计日志、输入校验、事务
    • API 层:幂等键、统一异常→HTTP 响应映射
    • 单分派:functools.singledispatch(基于参数类型选择实现)

内置好物:

from functools import lru_cache, cached_property, singledispatch

  1. 易踩坑与最佳实践
    1. 务必 @wraps:保留元数据(name/doc/签名)与调试体验。
    2. 导入时副作用:装饰器在导入时运行,避免做重 IO/重计算。
    3. 参数透传:wrapper 一律 *args, **kwargs,不要漏参/改名。
    4. 异常处理:不要“吞异常”;需要时只捕获你预期的异常并加上下文再抛。
    5. 顺序:多个装饰器叠加时注意 A(B(f)) 的组合顺序。
    6. 同步/异步匹配:装 async def 就写 async wrapper,否则会把协程对象当返回值。
    7. 类型安全:用 ParamSpec/TypeVar,避免把函数都变成 Callable[…, Any]。
    8. 可测试性:把装饰器影响的行为暴露开关(例如 enabled=False、或提供未装饰版本用于测试)。

一句话收尾

装饰器让你“不改源码、横切增强”:统一把计时/重试/鉴权/缓存/日志等横切逻辑抽离出来;
记住三件宝:@wraps、*args/**kwargs、ParamSpec,写出来的装饰器就既好用又专业。

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

MonkeyKing.sun

对你有帮助的话,可以打赏

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值