当前位置:首页>python>Python: 这可能是全网最友好的Python装饰器教程(内附4大实战场景)

Python: 这可能是全网最友好的Python装饰器教程(内附4大实战场景)

  • 2026-10-11 08:05:20
Python: 这可能是全网最友好的Python装饰器教程(内附4大实战场景)
上次我们聊了闭包,说它是“内层函数+外层变量”的组合。今天要讲的装饰器,就是闭包最闪亮的应用——它能让你在不修改原函数的情况下,给函数统一加上日志、计时、校验、缓存等功能。

读完你会:

  • 闭包是怎么“升级”成装饰器的

  • *args、**kwargs 在装饰器里怎么用

  • 带参数的装饰器怎么写

  • 多个装饰器同时用的顺序问题

  • 类装饰器又是什么

  • 用几个真实场景玩转装饰器

一、什么是函数装饰器?

用一句话定义:

装饰器是一个可调用对象(通常是函数),它接收一个函数作为参数,并返回一个新函数。

装饰器的核心作用,就是在不修改原函数代码的前提下,增强或改变原函数的功能。

这背后依赖的就是闭包:外层的装饰器函数捕获了被装饰的函数,返回一个内层的 wrapper 函数,以后调用原函数,其实都是在调用 wrapper。

def my_decorator(func):          # 1. 接收被装饰的函数    def wrapper(*args, **kwargs): # 3. 用 *args 和 **kwargs 接收任意参数        print("前置增强逻辑...")        result = func(*args, **kwargs) # 4. 调用原函数        print("后置增强逻辑...")        return result            # 4. 记得把原函数的返回值 return 出去    return wrapper               # 2. 返回新函数(wrapper)@my_decoratordef greet(name):    return f"Hello, {name}!"print(greet("Alice"))
@my_decorator 等价于 greet = my_decorator(greet)。从此 greet 这个变量名,指向的是 wrapper 函数,但原来的功能一点儿没少,还被“包裹”了一层。
  1. 接收被装饰函数,返回新函数(wrapper) —— 装饰器本质就是一个“函数工厂”。

  2. 装饰器“吐出来”的是 wrapper,以后别人调用的也是 wrapper。

  3. 用 *args, **kwargs 保证参数兼容性 —— 你不知道原函数需要几个参数,用万能参数接收准没错。

  4. wrapper 里要调用原函数,并把返回值 return 出去 —— 否则原函数的返回值就丢了。

缺了任何一点,装饰器就可能出 bug。

二、带参数的装饰器

有时候我们希望装饰器能接收一些配置参数,比如“这个日志要输出到哪个文件”。这时候需要在普通装饰器外面再包一层函数。

def log_to_file(filename):    # 最外层负责接收参数,并返回真正的装饰器    def decorator(func):        def wrapper(*args, **kwargs):            result = func(*args, **kwargs)            with open(filename, 'a') as f:                f.write(f"{func.__name__} 被调用,结果:{result}\n")            return result        return wrapper    return decorator@log_to_file("app.log")def add(a, b):    return a + bprint(add(3, 4))  # 同时会在 app.log 里写入一条记录

原理:@log_to_file("app.log") 相当于 add = log_to_file("app.log")(add)。最外层接收 "app.log",返回 decorator,然后 decorator(add) 再返回最终的 wrapper。三层函数,逻辑依然清晰。

三、多个装饰器一起用,顺序怎么记

@decorator_a@decorator_bdef my_func():    pass

执行与调用顺序:

  • 装饰器函数本身从上往下执行:先执行 decorator_a(func_b)?等等,这里有点绕。实际上代码等价于:

my_func = decorator_a(decorator_b(my_func))

所以靠近函数定义的 decorator_b 会先执行装饰动作,decorator_a 后执行。

  • 调用时,wrapper 从外到内执行:最外面的 wrapper 先运行,然后逐层向内。

def dec_a(func):    print("装饰器 A 执行")    def wrapper(*args, **kwargs):        print("进入 wrapper A")        result = func(*args, **kwargs)        print("离开 wrapper A")        return result    return wrapperdef dec_b(func):    print("装饰器 B 执行")    def wrapper(*args, **kwargs):        print("进入 wrapper B")        result = func(*args, **kwargs)        print("离开 wrapper B")        return result    return wrapper@dec_a@dec_bdef say():    print("原函数执行中...")say()
记住口诀:装饰时从下往上,调用时从上往下(先外后内)。
四、类装饰器

如果你觉得函数套函数有点绕,或者装饰器本身需要维护状态,可以用类装饰器。

只要一个类实现了 __call__ 方法,它的实例就可以像函数一样被调用。用这个实例作为装饰器,就能工作。

class CountCalls:    def __init__(self, func):        self.func = func        self.count = 0                # 状态记录    def __call__(self, *args, **kwargs):        self.count += 1        print(f"{self.func.__name__} 已被调用 {self.count} 次")        return self.func(*args, **kwargs)@CountCallsdef hello():    print("你好!")hello()hello()

原理解析:
@CountCalls 相当于 hello = CountCalls(hello),此时 hello 变成了一个 CountCalls 的实例。调用 hello() 时,会自动触发 __call__ 方法,方法里再调用原函数。

类装饰器特别适合需要记录状态(比如计数器、缓存)的场景。

五、实战:四行装饰器搞定日志、计时、校验、缓存

## 通用日志装饰器def logger(func):    def wrapper(*args, **kwargs):        print(f"[LOG] 调用 {func.__name__}({args}, {kwargs})")        return func(*args, **kwargs)    return wrapper
## 性能计时装饰器import timedef timer(func):    def wrapper(*args, **kwargs):        start = time.time()        result = func(*args, **kwargs)        print(f"[TIMER] {func.__name__} 耗时 {time.time()-start:.4f} 秒")        return result    return wrapper
## 参数校验装饰器def check_positive(func):    def wrapper(*args, **kwargs):        for arg in args:            if isinstance(arg, (int, float)) and arg < 0:                raise ValueError("参数不能为负数")        return func(*args, **kwargs)    return wrapper
## 简易缓存装饰器def cache(func):    memo = {}    def wrapper(*args):        if args not in memo:            memo[args] = func(*args)        return memo[args]    return wrapper@cachedef fib(n):    if n <= 1: return n    return fib(n-1) + fib(n-2)print(fib(30))  # 瞬间出结果
六、总结
  • 装饰器 = 闭包的经典应用,用一个可调用对象包裹另一个函数,增强其行为。

  • 基础结构永远不变:接收函数 → 返回 wrapper → wrapper 里调用原函数并处理增强逻辑。

  • 带参数的装饰器 再加一层函数传参即可。

  • 多装饰器 叠加时注意装饰顺序(从下往上)和调用顺序(从上往下)。

  • 类装饰器 让装饰器能够持有状态,结构更直观。

  • 装饰器在 Web 框架、权限校验、日志、缓存等领域无处不在,是 Pythonic 代码的重要标志。

掌握了闭包和装饰器,你就掌握了 Python 函数式编程的一大半,也为理解后面的类装饰器、functools.wraps 等高级话题铺平了道路。


如果觉得这篇文章对你有帮助,欢迎点赞、在看、转发给同样在学习 Python 的朋友~

谢谢~~~

最新文章

随机文章