当前位置:首页>python>Python编程基础(装饰器)

Python编程基础(装饰器)

  • 2026-10-06 22:00:05
Python编程基础(装饰器)

一、装饰器是什么?

装饰器(Decorator)是 Python 提供的一种在不修改原函数(或类)源代码的前提下,动态地为其添加额外功能的语法机制。它本质上是一个接收函数作为参数,并返回一个新函数的高阶函数,通过 `@decorator_name` 语法糖应用到目标对象上。

二、为什么需要装饰器?

看一个经典的对比场景:假设我们要给多个函数都加上"打印执行日志"的功能。

如果不使用装饰器:

# ❌ 不使用装饰器:每个函数都要手动写重复代码def add(a, b):    print("函数 add 开始执行")    result = a + b    print("函数 add 执行结束")    return resultdef sub(a, b):    print("函数 sub 开始执行")    result = a - b    print("函数 sub 执行结束")    return result# 每加一个函数都要复制粘贴日志代码 → 违反 DRY 原则

如果使用装饰器:

# ✅ 使用高阶函数:可以复用逻辑,但调用方式改变了def log(func):    def wrapper(a, b):        print(f"函数 {func.__name__} 开始执行")        result = func(a, b)        print(f"函数 {func.__name__} 执行结束")        return result    return wrapperadd = log(add)   # 必须手动重新赋值,不够优雅# ✅ 使用装饰器:简洁、优雅、非侵入式@logdef add(a, b):    return a + b# @log 等价于 add = log(add),一行搞定

三、装饰器的工作原理

装饰器背后依赖 Python 的两个核心特性:

特性

说明

函数是一等对象

函数可以被赋值、作为参数传递、作为返回值

闭包(Closure)

内部函数可以捕获并记住外部函数作用域中的变量

语法糖的等价转换

@decoratordef func():    pass# 完全等价于:def func():    passfunc = decorator(func)

装饰器的三要素

- 接收函数:装饰器把被装饰的函数作为参数

- 包装函数:内部定义一个 `wrapper` 来增强原函数

- 返回函数:返回 `wrapper` 替代原函数

四、手写一个装饰器 —— 三个由浅入深的例子

例子 1:日志装饰器(基础版)

最经典的入门场景:为函数自动打印调用日志。

from functools import wrapsdef log(func):    @wraps(func)  # 保留原函数的 __name__、__doc__ 等元信息    def wrapper(*args, **kwargs):        print(f"[LOG] 调用 {func.__name__},参数: args={args}, kwargs={kwargs}")        result = func(*args, **kwargs)        print(f"[LOG] {func.__name__} 返回: {result}")        return result    return wrapper# 使用@logdef add(a, b):    """两数相加"""    return a + badd(3, 5)print(add.__name__)  # 因为用了 @wraps,输出仍然是 'add'

运行输出:

[LOG] 调用 add,参数: args=(3, 5), kwargs={}[LOG] add 返回: 8add

💡 关键点:`@wraps(func)` 用于保留原函数的元信息(如 `name`、`doc`),是编写装饰器的最佳实践。

例子 2:计时装饰器(实用版)

统计函数运行耗时,是性能分析中非常常用的工具。

import timefrom functools import wrapsdef timer(func):    @wraps(func)    def wrapper(*args, **kwargs):        start = time.time()        result = func(*args, **kwargs)        elapsed = time.time() - start        print(f"函数 {func.__name__} 耗时: {elapsed:.4f} 秒")        return result    return wrapper# 使用@timerdef heavy_calc(n):    return sum(i * i for i in range(n))heavy_calc(1_000_000)

输出:

函数 heavy_calc 耗时: 0.0812 秒

例子 3:带参数的装饰器(进阶版)

有时我们希望装饰器本身也能接收参数(比如"重试次数"、"日志级别")。这时需要多套一层函数。

from functools import wrapsimport timedef retry(times=3, delay=1):    """带参数的装饰器:失败自动重试"""    def decorator(func):        @wraps(func)        def wrapper(*args, **kwargs):            last_exc = None            for i in range(1, times + 1):                try:                    return func(*args, **kwargs)                except Exception as e:                    last_exc = e                    print(f"[第 {i} 次尝试失败] {func.__name__}: {e}")                    time.sleep(delay)            print(f"[放弃] {func.__name__} 已重试 {times} 次仍失败")            raise last_exc        return wrapper    return decorator# 使用@retry(times=3, delay=0.5)def unstable_request():    import random    if random.random() < 0.7:        raise ConnectionError("网络不稳定")    return "请求成功"print(unstable_request())

可能的输出(此处有随机值,所以每次输出结果不一致,可多运行几次查看结果):

[第 1 次尝试失败] unstable_request: 网络不稳定[第 2 次尝试失败] unstable_request: 网络不稳定请求成功

> 💡 理解三层结构:

  • 最外层 retry(times, delay):接收装饰器参数,返回真正的装饰器

  • 中间层 decorator(func):接收被装饰函数,返回 wrapper

  • 最内层 wrapper(*args, **kwargs):真正执行的逻辑

五、装饰器的高级用法

1. 多个装饰器叠加(由内向外应用)

@log@timerdef foo():    pass# 等价于:foo = log(timer(foo))# 执行顺序:先进入 log 的 wrapper,再进入 timer 的 wrapper,最后调用 foo

2. 类装饰器(用类实现装饰器)

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 greet():    print("Hello!")greet()  # greet 第 1 次被调用greet()  # greet 第 2 次被调用

3. 常见的内置/标准库装饰器

装饰器

作用

@staticmethod

将类方法声明为静态方法

@classmethod

将类方法声明为类方法

@property

将方法变为属性访问

@functools.lru_cache

自动缓存函数返回值

@functools.wraps

保留被装饰函数的元信息

@dataclasses.dataclass

自动生成类的 __init__ 等方法

六、总结

要点

说明

本质

一个"接收函数、返回函数"的高阶函数

基础

依赖闭包与"函数是一等对象"的特性

语法

`@decorator` 是 `func = decorator(func)` 的语法糖

优势

复用逻辑、非侵入式、代码简洁、职责分离

最佳实践

使用 `@functools.wraps` 保留原函数元信息

应用场景

日志、计时、缓存、权限校验、重试、事务、路由注册等

一句话记忆:装饰器 = 一个"给函数穿衣服"的函数,在不改变函数本身的情况下,为它加上额外的能力。

最新文章

随机文章