# 装饰器基础

URL: https://caijiao.org/python/ch10/decorators
Source: docs/python/ch10/decorators.md
Description: 本章介绍 Python 中的装饰器基础，讲解装饰器的定义、使用方法及其在函数编程中的应用。

在 [Python](/python/) 中，装饰器是一种设计模式，允许在不修改函数代码的情况下，动态地给函数或方法添加功能。装饰器本质上是一个返回函数的函数，它通常用于日志记录、性能测试、权限检查等场景。

## 装饰器的基本概念

装饰器是一种用于修改或扩展函数、方法或类功能的工具。通过装饰器，我们可以在函数执行前后插入自定义代码。

### 装饰器的语法

装饰器通常以 `@decorator_name` 的形式应用于函数前面。其本质是将目标函数作为参数传递给装饰器函数，并返回一个新的函数或方法。

```python
def decorator(func):
    def wrapper():
        print("函数执行前")
        func()  # 调用原函数
        print("函数执行后")
    return wrapper

@decorator
def say_hello():
    print("Hello!")

say_hello()
```

输出：

```
函数执行前
Hello!
函数执行后
```

在上面的例子中，`say_hello` 函数被 `decorator` 装饰器装饰。装饰器通过包装原函数，在执行之前和之后添加了额外的操作。

## 装饰器的工作原理

装饰器本质上是一个高阶函数。它接收一个函数作为输入，并返回一个新的函数。在这个新的函数中，可以在调用原函数之前或之后加入一些额外的行为。

### 装饰器的结构

装饰器的结构通常如下所示：

```python
def decorator(func):
    def wrapper(*args, **kwargs):
        # 在函数执行之前做一些操作
        result = func(*args, **kwargs)  # 调用原函数
        # 在函数执行之后做一些操作
        return result
    return wrapper
```

### 应用装饰器

装饰器通过 `@decorator_name` 语法应用于函数，如下所示：

```python
@decorator
def some_function():
    pass
```

这相当于：

```python
some_function = decorator(some_function)
```

## 带参数的装饰器

装饰器也可以接受参数，允许我们在使用时传递动态信息。例如，下面是一个带有参数的装饰器：

```python
def repeat(num_times):
    def decorator(func):
        def wrapper(*args, **kwargs):
            for _ in range(num_times):
                func(*args, **kwargs)
        return wrapper
    return decorator

@repeat(3)
def say_hello():
    print("Hello!")

say_hello()
```

输出：

```
Hello!
Hello!
Hello!
```

在这个例子中，`repeat` 装饰器接受一个参数 `num_times`，表示函数执行的次数。

## 装饰器与函数签名

使用装饰器时，我们通常会遇到装饰器修改函数签名的问题。为了保留原函数的签名，Python 提供了 `functools.wraps` 工具。`functools.wraps` 用于复制被装饰函数的元数据（如函数名、文档字符串等）到包装函数上。

```python
import functools

def decorator(func):
    @functools.wraps(func)  # 保留原函数的信息
    def wrapper(*args, **kwargs):
        print("函数执行前")
        result = func(*args, **kwargs)
        print("函数执行后")
        return result
    return wrapper

@decorator
def say_hello():
    """打印问候"""
    print("Hello!")

print(say_hello.__name__)  # 输出：say_hello
print(say_hello.__doc__)  # 输出：打印问候
```

使用 `functools.wraps` 后，装饰器不会改变原函数的名称和文档字符串。

## 总结

- 装饰器是一种设计模式，允许在不修改函数代码的情况下，动态地添加功能。
- 装饰器本质是一个接收函数作为参数并返回一个新函数的高阶函数。
- 使用 `@decorator_name` 语法可以轻松应用装饰器。
- 装饰器可以带参数，灵活性高。
- 使用 `functools.wraps` 可以保留被装饰函数的元数据，如函数名称和文档字符串。

装饰器是 Python 中非常强大和灵活的工具，广泛应用于 Web 开发、日志记录、权限检查等领域。
