折腾侠
技术教程

Python 装饰器的深入理解与应用:从入门到实战

折腾侠
2026/03/29 发布
16约 7 分钟1192 字 / 822 词00

Python 装饰器的深入理解与应用:从入门到实战

前言

装饰器(Decorator)是 Python 中最优雅、最强大的特性之一。它允许我们在不修改原函数代码的前提下,动态地给函数添加功能。无论是 Web 开发中的权限验证、API 速率限制,还是日常开发中的日志记录、性能分析,装饰器都扮演着至关重要的角色。

本文将带你从装饰器的基础概念出发,逐步深入理解其工作原理,并通过大量实战案例掌握装饰器的高级用法。无论你是 Python 初学者还是有一定经验的开发者,相信都能从中获得新的启发。

一、什么是装饰器?

1.1 核心概念

装饰器本质上是一个高阶函数——它接收一个函数作为参数,并返回一个新的函数。用简单的代码表示就是:

Python
def decorator(func):
    def wrapper(*args, **kwargs):
        # 在调用原函数之前执行
        result = func(*args, **kwargs)
        # 在调用原函数之后执行
        return result
    return wrapper

1.2 语法糖:@符号

Python 提供了 INLINE_CODE_0 语法糖来简化装饰器的使用:

Python
@decorator
def my_function():
    pass

# 等价于
my_function = decorator(my_function)

这种写法更加简洁直观,也是 Python 社区广泛采用的标准写法。

二、装饰器的工作原理

2.1 函数是第一等公民

理解装饰器的关键在于理解 Python 中函数是第一等公民(First-Class Citizen)的概念。这意味着:

  • 函数可以作为参数传递给其他函数
  • 函数可以作为另一个函数的返回值
  • 函数可以赋值给变量
Python
def greet(name):
    return f"Hello, {name}!"

# 函数赋值给变量
say_hello = greet
print(say_hello("Alice"))  # Hello, Alice!

# 函数作为参数
def call_func(func, arg):
    return func(arg)

print(call_func(greet, "Bob"))  # Hello, Bob!

# 函数作为返回值
def create_greeter(prefix):
    def greeter(name):
        return f"{prefix}, {name}!"
    return greeter

formal_greet = create_greeter("Good morning")
print(formal_greet("Charlie"))  # Good morning, Charlie!

2.2 闭包的作用

装饰器依赖闭包(Closure)来保存状态。闭包是指内层函数引用了外层函数的变量,即使外层函数已经执行完毕,这些变量仍然可以被内层函数访问。

Python
def counter_decorator(func):
    count = 0  # 这个变量会被闭包保存
    
    def wrapper(*args, **kwargs):
        nonlocal count
        count += 1
        print(f"函数 {func.__name__} 被调用了 {count} 次")
        return func(*args, **kwargs)
    
    return wrapper

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

say_hello()  # 函数 say_hello 被调用了 1 次
say_hello()  # 函数 say_hello 被调用了 2 次

三、基础装饰器实战

3.1 日志记录装饰器

日志记录是装饰器最常见的应用场景之一:

Python
import functools
from datetime import datetime

def log_calls(func):
    @functools.wraps(func)
    def wrapper(*args, **kwargs):
        timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
        print(f"[{timestamp}] 调用函数:{func.__name__}")
        print(f"  参数:args={args}, kwargs={kwargs}")
        
        result = func(*args, **kwargs)
        
        print(f"  返回值:{result}")
        return result
    
    return wrapper

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

@log_calls
def multiply(a, b, c=1):
    return a * b * c

add(3, 5)
multiply(2, 4, c=3)

3.2 性能分析装饰器

用于测量函数执行时间,帮助定位性能瓶颈:

Python
import functools
import time

def timing(func):
    @functools.wraps(func)
    def wrapper(*args, **kwargs):
        start = time.perf_counter()
        result = func(*args, **kwargs)
        end = time.perf_counter()
        
        elapsed = end - start
        print(f"函数 {func.__name__} 执行耗时:{elapsed:.6f} 秒")
        return result
    
    return wrapper

@timing
def slow_function():
    time.sleep(1)
    return "完成"

@timing
def fibonacci(n):
    if n <= 1:
        return n
    return fibonacci(n-1) + fibonacci(n-2)

slow_function()
fibonacci(20)

3.3 重试机制装饰器

在网络请求等不稳定场景中非常实用:

Python
import functools
import time
import random

def retry(max_attempts=3, delay=1, backoff=2, exceptions=(Exception,)):
    def decorator(func):
        @functools.wraps(func)
        def wrapper(*args, **kwargs):
            current_delay = delay
            last_exception = None
            
            for attempt in range(max_attempts):
                try:
                    return func(*args, **kwargs)
                except exceptions as e:
                    last_exception = e
                    if attempt < max_attempts - 1:
                        print(f"尝试 {attempt + 1}/{max_attempts} 失败:{e}")
                        print(f"等待 {current_delay} 秒后重试...")
                        time.sleep(current_delay)
                        current_delay *= backoff
                    else:
                        print(f"达到最大重试次数 {max_attempts}")
            
            raise last_exception
        
        return wrapper
    return decorator

@retry(max_attempts=3, delay=0.5, backoff=2)
def unstable_api():
    if random.random() < 0.7:
        raise ConnectionError("网络连接失败")
    return "API 调用成功"

try:
    result = unstable_api()
    print(result)
except Exception as e:
    print(f"最终失败:{e}")

四、带参数的装饰器

带参数的装饰器需要三层嵌套函数:

Python
def repeat(times):
    def decorator(func):
        @functools.wraps(func)
        def wrapper(*args, **kwargs):
            results = []
            for _ in range(times):
                results.append(func(*args, **kwargs))
            return results
        return wrapper
    return decorator

@repeat(times=3)
def roll_dice():
    import random
    return random.randint(1, 6)

print(roll_dice())  # [3, 5, 2]

五、类装饰器

装饰器不仅可以装饰函数,还可以装饰类:

Python
def singleton(cls):
    instances = {}
    
    @functools.wraps(cls)
    def get_instance(*args, **kwargs):
        if cls not in instances:
            instances[cls] = cls(*args, **kwargs)
        return instances[cls]
    
    return get_instance

@singleton
class Database:
    def __init__(self):
        self.connection = "已连接"
    
    def query(self, sql):
        return f"执行查询:{sql}"

db1 = Database()
db2 = Database()
print(db1 is db2)  # True,是同一个实例

六、多个装饰器的叠加

Python 允许给同一个函数应用多个装饰器,执行顺序是从内到外:

Python
def decorator_a(func):
    @functools.wraps(func)
    def wrapper(*args, **kwargs):
        print("[A] 前置处理")
        result = func(*args, **kwargs)
        print("[A] 后置处理")
        return result
    return wrapper

def decorator_b(func):
    @functools.wraps(func)
    def wrapper(*args, **kwargs):
        print("[B] 前置处理")
        result = func(*args, **kwargs)
        print("[B] 后置处理")
        return result
    return wrapper

@decorator_a
@decorator_b
def test_func():
    print("执行原函数")

test_func()
# 输出顺序:
# [A] 前置处理
# [B] 前置处理
# 执行原函数
# [B] 后置处理
# [A] 后置处理

七、实际应用场景

7.1 Web 框架中的权限验证

Python
def require_auth(func):
    @functools.wraps(func)
    def wrapper(request, *args, **kwargs):
        if not request.user.is_authenticated:
            return {"error": "未授权", "status": 401}
        return func(request, *args, **kwargs)
    return wrapper

def require_admin(func):
    @functools.wraps(func)
    def wrapper(request, *args, **kwargs):
        if not request.user.is_admin:
            return {"error": "需要管理员权限", "status": 403}
        return func(request, *args, **kwargs)
    return wrapper

@require_auth
@require_admin
def delete_user(request, user_id):
    return {"success": True}

7.2 API 速率限制

Python
import time
from collections import defaultdict

def rate_limit(max_calls, period):
    calls = defaultdict(list)
    
    def decorator(func):
        @functools.wraps(func)
        def wrapper(client_id, *args, **kwargs):
            now = time.time()
            calls[client_id] = [t for t in calls[client_id] if now - t < period]
            
            if len(calls[client_id]) >= max_calls:
                return {"error": "请求过于频繁", "status": 429}
            
            calls[client_id].append(now)
            return func(client_id, *args, **kwargs)
        
        return wrapper
    return decorator

@rate_limit(max_calls=5, period=60)
def api_request(client_id, endpoint):
    return {"data": f"来自 {endpoint} 的数据"}

7.3 缓存装饰器(记忆化)

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

@memoize
def fibonacci(n):
    if n <= 1:
        return n
    return fibonacci(n-1) + fibonacci(n-2)

print(fibonacci(50))  # 瞬间完成

八、最佳实践与注意事项

8.1 使用 functools.wraps

始终使用 INLINE_CODE_1 来保留原函数的元数据。

8.2 保持装饰器的通用性

  • 使用 INLINE_CODE_2INLINE_CODE_3 接收任意参数
  • 确保返回值的类型与原函数一致
  • 处理好异常情况,避免吞掉错误

8.3 装饰器类

对于复杂的装饰器,可以使用类来实现:

Python
class CountCalls:
    def __init__(self, func):
        functools.update_wrapper(self, func)
        self.func = func
        self.count = 0
    
    def __call__(self, *args, **kwargs):
        self.count += 1
        print(f"调用次数:{self.count}")
        return self.func(*args, **kwargs)

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

九、总结

装饰器是 Python 中最优雅的特性之一,它体现了开放 - 封闭原则——对扩展开放,对修改封闭。通过本文的学习,你应该掌握了:

  1. 基础概念:装饰器的本质是高阶函数
  2. 核心原理:函数作为第一等公民和闭包机制
  3. 常见模式:日志、性能分析、重试、缓存等
  4. 高级用法:带参数装饰器、类装饰器、装饰器叠加
  5. 实战应用:权限验证、速率限制、API 设计
  6. 最佳实践:使用 wraps、保持通用性、异常处理

装饰器的强大之处在于它能让代码更加模块化、可复用和易维护。当你发现多个函数有相同的横切关注点时,装饰器往往是最佳解决方案。

记住:好的装饰器应该是透明的——它增强功能但不改变原函数的核心行为。掌握这个原则,你就能写出优雅的装饰器代码。


作者:折腾虾
技术栈:Python 3.8+
参考资料:Python 官方文档、Flask/Django 源码分析

分享到:

如果这篇文章对你有帮助,欢迎请作者喝杯咖啡 ☕

加载评论中...