Python装饰器怎么写_装饰器原理与实战示例

4次阅读

python装饰器是接收函数并返回新函数的高阶函数,核心是不修改原函数代码而增强功能;@语法糖等价于func = decorator(func);基本写法含嵌套函数,需用functools.wraps保留元信息;类装饰器适合维护状态;常见应用包括权限校验、缓存和重试机制。

Python装饰器怎么写_装饰器原理与实战示例

Python装饰器本质是接收函数作为参数、返回新函数的高阶函数,核心在于“不修改原函数代码的前提下增强其功能”。关键点是理解 @ 语法糖只是语法简写,等价于 func = decorator(func)

装饰器的基本写法(带参/不带参)

最简装饰器是一个嵌套函数:外层接收被装饰函数,内层定义执行逻辑(常含 *args, **kwargs 以兼容任意参数),最后返回内层函数。

示例(无参装饰器):

def log_call(func):     def wrapper(*args, **kwargs):         print(f"调用函数: {func.__name__}")         result = func(*args, **kwargs)         print(f"{func.__name__} 执行完毕")         return result     return wrapper <p>@log_call def greet(name): return f"Hello, {name}!"</p><p>print(greet("Alice"))</p><p><span>立即学习</span>“<a href="https://pan.quark.cn/s/00968c3c2c15" style="text-decoration: underline !important; color: blue; font-weight: bolder;" rel="nofollow" target="_blank">Python免费学习笔记(深入)</a>”;</p>

输出会先打印调用信息,再执行原函数,最后打印结束信息。

若需给装饰器传参(如指定日志级别),需再加一层封装

  • 最外层接收装饰器参数(如 level="INFO"
  • 中间层接收被装饰函数
  • 最内层是实际执行逻辑

保留原函数元信息(__name__、__doc__等)

直接写装饰器会导致 greet.__name__ 变成 "wrapper",影响调试和文档生成。解决方法是使用 functools.wraps

from functools import wraps <p>def log_call(func): @wraps(func)  # 关键!复制原函数的元信息 def wrapper(*args, *<em>kwargs): print(f"调用函数: {func.<strong>name</strong>}") return func(</em>args, **kwargs) return wrapper</p>

加了 @wraps(func) 后,greet.__name__ 仍为 "greet"greet.__doc__ 也能正常显示。

类实现装饰器(支持状态与复用)

当需要在装饰过程中维护状态(如统计调用次数),用类更清晰。类需实现 __call__ 方法:

class CountCalls:     def __init__(self):         self.count = 0 <pre class="brush:php;toolbar:false;">def __call__(self, func):     @wraps(func)     def wrapper(*args, **kwargs):         self.count += 1         print(f"{func.__name__} 已被调用 {self.count} 次")         return func(*args, **kwargs)     return wrapper

counter = CountCalls()

@counter def say_hi(): print(“Hi!”)

say_hi() # 输出:say_hi 已被调用 1 次 say_hi() # 输出:say_hi 已被调用 2 次

实战场景:权限校验与缓存装饰器

真实项目中常见需求可快速封装为装饰器:

  • 权限检查:在函数执行前验证用户角色,不满足则抛出异常或返回错误
  • 结果缓存:对纯计算函数(如斐波那契),用字典缓存已算结果,避免重复耗时运算
  • 重试机制:对可能失败的网络请求,自动重试指定次数并捕获异常

例如缓存装饰器简化版:

def cache(func):     cached = {}     @wraps(func)     def wrapper(n):         if n not in cached:             cached[n] = func(n)         return cached[n]     return wrapper <p>@cache def fib(n): if n < 2: return n return fib(n-1) + fib(n-2)</p>

这样调用 fib(35) 不会卡住,因为子问题被缓存复用。

text=ZqhQzanResources