Sat Mar 01 2025 教程
2552 words · 11 minutes

Python 装饰器完全指南


Table of Contents

📚 参考资源 Link to 📚 参考资源

学习过程中参考了以下优质资源:


🤔 什么是 Python 装饰器? Link to 🤔 什么是 Python 装饰器?

核心概念 Link to 核心概念

  • 本质:装饰器本身是一个可调用对象(函数或类),接收一个函数/类作为参数,返回修改后的函数/类
  • 作用:为代码提供非侵入式的功能扩展(如日志、权限校验、性能测试等)
  • 语法糖@decorator 等价于 func = decorator(func)

🧩 理解函数的本质 Link to 🧩 理解函数的本质

函数作为参数 Link to 函数作为参数

函数可以作为另一个函数的参数传递:

PYTHON
1
2
3
4
5
6
7
8
9
10
11
12
def double(x):
    return 2 * x

def triple(x):
    return 3 * x

def calc(func, x):
    """接收一个函数作为参数"""
    return func(x)

print(calc(double, 2))  # 输出: 4
print(calc(triple, 3))  # 输出: 9

函数作为返回值 Link to 函数作为返回值

函数也可以作为另一个函数的返回值:

PYTHON
1
2
3
4
5
6
7
8
9
10
11
12
def generate_func(n):
    """返回一个新的函数"""
    def multiple_func(x):
        return n * x
    return multiple_func

# 动态创建函数
double = generate_func(2)
triple = generate_func(3)

print(double(2))  # 输出: 4
print(triple(3))  # 输出: 9

🎯 装饰器的本质 Link to 🎯 装饰器的本质

最简单的装饰器 Link to 最简单的装饰器

PYTHON
1
2
3
4
5
6
7
8
9
10
def dec(f):
    """最简单的装饰器,直接返回原函数"""
    return f

@dec
def double(x):
    return x * 2

# 等价于
double = dec(double)

第一个实用装饰器 Link to 第一个实用装饰器

下面是一个为函数添加前后处理逻辑的装饰器:

PYTHON
1
2
3
4
5
6
7
8
9
10
11
12
13
14
def a_new_decorator(a_func):
    """在函数执行前后添加额外逻辑"""
    def wrapTheFunction():
        print("⏱️ 执行前的准备工作...")
        a_func()
        print("✅ 执行后的清理工作...")
    return wrapTheFunction

@a_new_decorator
def a_function_requiring_decoration():
    print("🎯 这是核心功能")

# 调用被装饰的函数
a_function_requiring_decoration()

输出:

PLAINTEXT
1
2
3
⏱️ 执行前的准备工作...
🎯 这是核心功能
✅ 执行后的清理工作...

🔧 正确使用 functools.wraps Link to 🔧 正确使用 functools.wraps

问题演示 Link to 问题演示

PYTHON
1
2
3
4
5
6
7
8
9
10
11
def a_new_decorator(a_func):
    def wrapTheFunction():
        a_func()
    return wrapTheFunction

@a_new_decorator
def my_function():
    """我是文档字符串"""
    pass

print(my_function.__name__)  # 输出: wrapTheFunction ❌ 错误!

解决方案 Link to 解决方案

PYTHON
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
from functools import wraps

def a_new_decorator(a_func):
    @wraps(a_func)  # ✅ 保留原函数的元信息
    def wrapTheFunction():
        print("执行前...")
        a_func()
        print("执行后...")
    return wrapTheFunction

@a_new_decorator
def my_function():
    """我是文档字符串"""
    pass

print(my_function.__name__)  # 输出: my_function ✅ 正确!

💼 装饰器的实战场景 Link to 💼 装饰器的实战场景

1️⃣ 标准装饰器模板 Link to 1️⃣ 标准装饰器模板

PYTHON
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
from functools import wraps

def decorator_name(f):
    @wraps(f)
    def decorated(*args, **kwargs):
        # 执行前的逻辑
        if not can_run:
            return "Function will not run"

        # 调用原函数
        result = f(*args, **kwargs)

        # 执行后的逻辑
        return result

    return decorated

@decorator_name
def func():
    return "Function is running"

2️⃣ 性能计时器 Link to 2️⃣ 性能计时器

记录函数执行时间的装饰器:

PYTHON
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
import time
from functools import wraps

def timeit(f):
    """计算函数执行时间"""
    @wraps(f)
    def wrapper(*args, **kwargs):
        start = time.time()
        result = f(*args, **kwargs)
        elapsed = time.time() - start
        print(f"⏱️ {f.__name__} 执行时间: {elapsed:.4f} 秒")
        return result
    return wrapper

@timeit
def slow_function():
    time.sleep(1)
    return "Done!"

slow_function()  # 输出: ⏱️ slow_function 执行时间: 1.0012 秒

3️⃣ 权限验证 Link to 3️⃣ 权限验证

用于 Web 应用的授权装饰器:

PYTHON
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
from functools import wraps

def requires_auth(f):
    """检查用户是否已授权"""
    @wraps(f)
    def decorated(*args, **kwargs):
        auth = request.authorization
        if not auth or not check_auth(auth.username, auth.password):
            return authenticate()  # 返回 401 错误
        return f(*args, **kwargs)
    return decorated

@requires_auth
def protected_route():
    return "这是受保护的页面"

4️⃣ 日志记录 Link to 4️⃣ 日志记录

自动记录函数调用的装饰器:

PYTHON
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
from functools import wraps

def logit(func):
    """记录函数调用"""
    @wraps(func)
    def with_logging(*args, **kwargs):
        print(f"📝 调用函数: {func.__name__}")
        print(f"   参数: args={args}, kwargs={kwargs}")
        result = func(*args, **kwargs)
        print(f"   返回值: {result}")
        return result
    return with_logging

@logit
def add(x, y):
    """计算两数之和"""
    return x + y

result = add(3, 5)

输出:

PLAINTEXT
1
2
3
📝 调用函数: add
   参数: args=(3, 5), kwargs={}
   返回值: 8

🎨 带参数的装饰器 Link to 🎨 带参数的装饰器

带参数的日志装饰器 Link to 带参数的日志装饰器

PYTHON
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
from functools import wraps

def logit(logfile='out.log'):
    """可配置日志文件的装饰器"""
    def logging_decorator(func):
        @wraps(func)
        def wrapped_function(*args, **kwargs):
            log_string = f"{func.__name__} was called"
            print(log_string)

            # 写入日志文件
            with open(logfile, 'a') as f:
                f.write(log_string + '\n')

            return func(*args, **kwargs)
        return wrapped_function
    return logging_decorator

@logit()  # 使用默认文件 out.log
def myfunc1():
    pass

@logit(logfile='func2.log')  # 自定义日志文件
def myfunc2():
    pass

myfunc1()  # 日志写入 out.log
myfunc2()  # 日志写入 func2.log

🏛️ 类装饰器 Link to 🏛️ 类装饰器

基础类装饰器 Link to 基础类装饰器

PYTHON
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
from functools import wraps

class logit(object):
    def __init__(self, logfile='out.log'):
        self.logfile = logfile

    def __call__(self, func):
        @wraps(func)
        def wrapped_function(*args, **kwargs):
            log_string = f"{func.__name__} was called"
            print(log_string)

            # 写入日志
            with open(self.logfile, 'a') as f:
                f.write(log_string + '\n')

            # 调用子类可能的扩展方法
            self.notify()

            return func(*args, **kwargs)
        return wrapped_function

    def notify(self):
        """空方法,供子类重写"""
        pass

继承扩展类装饰器 Link to 继承扩展类装饰器

PYTHON
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
class email_logit(logit):
    """
    扩展 logit,添加邮件通知功能
    """
    def __init__(self, email='admin@myproject.com', *args, **kwargs):
        self.email = email
        super(email_logit, self).__init__(*args, **kwargs)

    def notify(self):
        """发送邮件通知"""
        # 实际项目中这里会发送邮件
        print(f"📧 发送通知到: {self.email}")

@email_logit()
def critical_function():
    """关键函数,需要邮件通知"""
    print("执行关键操作...")

📋 总结 Link to 📋 总结

核心要点 Link to 核心要点

  • ✅ 装饰器本质是高阶函数,接收函数返回函数
  • ✅ 使用 @wraps 保留原函数的元信息
  • ✅ 装饰器常用于日志、计时、权限验证、缓存等场景
  • ✅ 带参数的装饰器需要三层嵌套
  • ✅ 类装饰器通过 __call__ 方法实现,便于扩展

最佳实践 Link to 最佳实践

  1. 始终使用 @wraps:保留函数元信息
  2. 使用 *args, **kwargs:支持任意参数
  3. 合理命名:装饰器名称应清晰表达功能
  4. 文档字符串:为装饰器添加清晰的说明
  5. 避免过度使用:不要滥用装饰器,保持代码可读性

进阶学习 Link to 进阶学习


Thanks for reading!

Python 装饰器完全指南

Sat Mar 01 2025 教程
2552 words · 11 minutes