Python装饰器详解:从基础到进阶

举报
yd_275638200 发表于 2026/10/28 14:10:21 2026/10/28
【摘要】 装饰器是Python中一个强大且常用的特性,它允许在不修改函数或类源码的情况下,动态地增加功能。无论是日志记录、性能计时、权限校验还是缓存,装饰器都能让代码更加简洁优雅。本文将带你从基础语法出发,逐步深入装饰器的进阶用法和实际应用。---### 一、什么是装饰器?装饰器本质上是一个可调用对象(通常是函数),它接收一个函数作为参数,并返回一个新的函数(或原函数)。使用 `@` 语法糖可以将装饰...

装饰器是Python中一个强大且常用的特性,它允许在不修改函数或类源码的情况下,动态地增加功能。无论是日志记录、性能计时、权限校验还是缓存,装饰器都能让代码更加简洁优雅。本文将带你从基础语法出发,逐步深入装饰器的进阶用法和实际应用。

---

### 一、什么是装饰器?

装饰器本质上是一个可调用对象(通常是函数),它接收一个函数作为参数,并返回一个新的函数(或原函数)。使用 `@` 语法糖可以将装饰器应用到目标函数上。

一个最简单的装饰器:

```python
def my_decorator(func):
    def wrapper():
        print("函数执行前")
        func()
        print("函数执行后")
    return wrapper

@my_decorator
def say_hello():
    print("Hello!")

say_hello()
# 输出:
# 函数执行前
# Hello!
# 函数执行后
```

等价于 `say_hello = my_decorator(say_hello)`。注意 `wrapper` 内部调用了原始的 `func`,但返回的是新的 `wrapper` 函数。

---

### 二、基础用法:处理带参数的函数

上面的装饰器只能用于无参函数。要支持任意参数,可以使用 `*args` 和 `**kwargs`:

```python
def log_decorator(func):
    def wrapper(*args, **kwargs):
        print(f"调用 {func.__name__},参数: args={args}, kwargs={kwargs}")
        result = func(*args, **kwargs)
        print(f"{func.__name__} 返回: {result}")
        return result
    return wrapper

@log_decorator
def add(a, b):
    return a + b

add(3, 5)
# 输出:
# 调用 add,参数: args=(3, 5), kwargs={}
# add 返回: 8
```

这里 `wrapper` 接收任意参数并原样传递给 `func`,同时返回 `func` 的结果。

---

### 三、带参数的装饰器

有时我们需要装饰器本身接受参数,例如 `@repeat(3)` 让函数执行3次。实现方式是写一个返回装饰器的函数(三层嵌套):

```python
def repeat(times):
    def decorator(func):
        def wrapper(*args, **kwargs):
            for _ in range(times):
                result = func(*args, **kwargs)
            return result
        return wrapper
    return decorator

@repeat(3)
def greet(name):
    print(f"Hello, {name}!")

greet("Alice")
# 输出三次 Hello, Alice!
```

`repeat(3)` 返回 `decorator`,然后 `decorator` 被应用到 `greet` 上。理解三层结构的关键:最外层接收装饰器参数,中间层接收被装饰函数,最内层是实际执行逻辑。

---

### 四、类装饰器

除了函数,类也可以作为装饰器。类装饰器需要实现 `__call__` 方法:

```python
class CountCalls:
    def __init__(self, func):
        self.func = func
        self.count = 0

    def __call__(self, *args, **kwargs):
        self.count += 1
        print(f"第 {self.count} 次调用 {self.func.__name__}")
        return self.func(*args, **kwargs)

@CountCalls
def say_hi():
    print("Hi!")

say_hi()
say_hi()
# 输出:
# 第 1 次调用 say_hi
# Hi!
# 第 2 次调用 say_hi
# Hi!
```

类装饰器适合需要维护状态的场景(如计数器、缓存)。`__init__` 接收被装饰函数,`__call__` 实现调用逻辑。

---

### 五、使用 `functools.wraps` 保留元信息

直接使用装饰器后,原函数的 `__name__`、`__doc__` 等元信息会被 `wrapper` 覆盖。例如:

```python
@log_decorator
def add(a, b):
    """加法函数"""
    return a + b

print(add.__name__)  # 输出 'wrapper',而不是 'add'
```

解决方法是使用 `functools.wraps` 装饰 `wrapper`:

```python
from functools import wraps

def log_decorator(func):
    @wraps(func)
    def wrapper(*args, **kwargs):
        # ... 逻辑 ...
        return func(*args, **kwargs)
    return wrapper
```

这样 `add.__name__` 和 `add.__doc__` 都会保留原函数的元信息。**强烈建议在所有装饰器中都使用 `@wraps(func)`**。

---

### 六、实际应用场景

#### 1. 性能计时

```python
import time
from functools import wraps

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

@timer
def slow_func():
    time.sleep(1)
    return "done"
```

#### 2. 权限校验(Web框架常见)

```python
def require_login(func):
    @wraps(func)
    def wrapper(user, *args, **kwargs):
        if not user.get("is_authenticated"):
            raise PermissionError("请先登录")
        return func(user, *args, **kwargs)
    return wrapper
```

#### 3. 缓存(LRU)

```python
from functools import lru_cache

@lru_cache(maxsize=128)
def fib(n):
    if n < 2:
        return n
    return fib(n-1) + fib(n-2)
```

`lru_cache` 是标准库中的装饰器,直接开箱即用。你也可以自己实现一个简单的缓存装饰器:

```python
def memoize(func):
    cache = {}
    @wraps(func)
    def wrapper(*args):
        if args not in cache:
            cache[args] = func(*args)
        return cache[args]
    return wrapper
```

---

### 七、装饰器执行顺序与注意事项

多个装饰器可以叠加使用,执行顺序是**从下到上**(由内向外):

```python
@decorator1
@decorator2
def func():
    pass
# 等价于 func = decorator1(decorator2(func))
```

因此 `decorator2` 先被应用,`decorator1` 后被应用。实际调用时,`decorator1` 的 wrapper 先执行,然后内部调用 `decorator2` 的 wrapper。

**注意事项**:
- 装饰器修改的是函数绑定,不是函数本身。原函数可以通过 `wrapper.__wrapped__` 访问(`functools.wraps` 会设置该属性)。
- 类的方法也可以被装饰,注意 `self` 会作为第一个参数传入 `wrapper`。
- 异步函数(`async def`)需要特殊的装饰器写法,或者使用支持协程的第三方库。

---

### 八、总结

装饰器是Python元编程的基石之一,它体现了“开放封闭原则”——对扩展开放,对修改封闭。掌握以下几点就能应付大多数场景:

- 用 `*args, **kwargs` 适配任意参数
- 用 `functools.wraps` 保留元信息
- 带参数的装饰器用三层嵌套
- 类装饰器通过 `__call__` 实现
- 多装饰器执行顺序由内向外

实际开发中,不要过度使用装饰器,保持代码可读性才是第一位的。希望本文能帮助你更自信地使用装饰器,写出更优雅的Python代码。

【声明】本内容来自华为云开发者社区博主,不代表华为云及华为云开发者社区的观点和立场。转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息,否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0)

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。