折腾侠
技术教程

Python 装饰器完全指南:从入门到实战

折腾侠
2026/04/27 发布
0约 7 分钟1391 字 / 668 词00

Python 装饰器完全指南:从入门到实战

引言

在 Python 编程的世界里,装饰器(Decorator)无疑是最优雅也最令人困惑的特性之一。许多初学者第一次见到 INLINE_CODE_0 符号时都会感到迷茫,而经验丰富的开发者则将其视为提升代码质量的利器。

装饰器本质上是一个函数,它接受另一个函数作为参数,并返回一个新的函数。这种"函数包装函数"的模式,让我们能够在不修改原函数代码的前提下,为其添加额外的功能。本文将带你深入理解装饰器的工作原理,并通过大量实战案例展示其强大之处。

一、装饰器的基础概念

1.1 什么是装饰器?

装饰器是一种设计模式,它允许程序员在不改变原函数代码的情况下,动态地为函数添加功能。想象一下,你有一个精美的礼物盒(原函数),装饰器就像是包装纸和丝带,你可以给礼物盒加上漂亮的包装,而不需要打开或改变盒子里的东西。

1.2 为什么需要装饰器?

在实际开发中,我们经常遇到这样的需求:

  • 在函数执行前后添加日志记录
  • 验证用户权限
  • 测量函数执行时间
  • 实现缓存机制
  • 处理异常和重试逻辑

如果没有装饰器,我们可能需要在每个函数中重复编写相同的代码。装饰器让我们能够将这些通用逻辑抽离出来,实现代码的复用和解耦。

二、理解装饰器的工作原理

2.1 函数是一等公民

要理解装饰器,首先要明白在 Python 中,函数是"一等公民"(First-Class Citizen)。这意味着:

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

say_hello = greet
print(say_hello())  # 输出:Hello!

# 函数可以作为参数
def execute(func):
    return func()

print(execute(greet))  # 输出:Hello!

# 函数可以作为返回值
def create_greeter():
    def greet():
        return "Hello!"
    return greet

greeter = create_greeter()
print(greeter())  # 输出:Hello!

2.2 装饰器的基本结构

一个最简单的装饰器由三部分组成:

  1. 外层函数(装饰器本身)
  2. 内层函数(包装函数)
  3. 返回内层函数
Python
def simple_decorator(func):
    """最简单的装饰器"""
    def wrapper():
        print("函数执行前")
        func()
        print("函数执行后")
    return wrapper

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

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

INLINE_CODE_1 语法糖等价于 INLINE_CODE_2

三、装饰器的进阶用法

3.1 带参数的装饰器

实际应用中,我们常常需要装饰器能够接受参数。这就需要三层嵌套:

Python
def repeat(times):
    """重复执行指定次数的装饰器"""
    def decorator(func):
        def wrapper(*args, **kwargs):
            for i in range(times):
                print(f"第 {i + 1} 次执行:")
                func(*args, **kwargs)
        return wrapper
    return decorator

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

greet("World")

3.2 保留原函数信息

使用装饰器后,原函数的 INLINE_CODE_3INLINE_CODE_4 会被包装函数覆盖。我们可以使用 INLINE_CODE_5 来解决这个问题:

Python
from functools import wraps

def logging_decorator(func):
    @wraps(func)
    def wrapper(*args, **kwargs):
        print(f"调用函数:{func.__name__}")
        return func(*args, **kwargs)
    return wrapper

@logging_decorator
def add(a, b):
    """两数相加"""
    return a + b

print(add.__name__)  # 输出:add(而不是 wrapper)
print(add.__doc__)   # 输出:两数相加

3.3 类装饰器

除了函数装饰器,Python 还支持类装饰器:

Python
class CountCalls:
    """统计函数调用次数的类装饰器"""
    def __init__(self, func):
        wraps(func)(self)
        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!")

say_hello()
say_hello()
print(f"总共调用 {say_hello.count} 次")

四、实战应用场景

4.1 性能监控装饰器

在开发中,我们经常需要知道某个函数的执行时间,以便进行性能优化

Python
import time
from functools import wraps

def timing_decorator(func):
    @wraps(func)
    def wrapper(*args, **kwargs):
        start = time.time()
        result = func(*args, **kwargs)
        end = time.time()
        print(f"{func.__name__} 执行时间:{end - start:.4f} 秒")
        return result
    return wrapper

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

slow_function()

4.2 缓存装饰器

对于计算密集型且结果可预测的函数,缓存可以大幅提升性能:

Python
from functools import wraps

def cache_decorator(func):
    cache = {}
    
    @wraps(func)
    def wrapper(*args):
        if args in cache:
            print(f"从缓存获取结果")
            return cache[args]
        result = func(*args)
        cache[args] = result
        return result
    return wrapper

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

print(fibonacci(10))  # 第一次计算
print(fibonacci(10))  # 从缓存获取

4.3 权限验证装饰器

在 Web 开发中,权限验证是常见需求:

Python
from functools import wraps

def require_permission(permission):
    def decorator(func):
        @wraps(func)
        def wrapper(user, *args, **kwargs):
            if permission not in user.get('permissions', []):
                raise PermissionError(f"用户缺少 {permission} 权限")
            return func(user, *args, **kwargs)
        return wrapper
    return decorator

@require_permission('admin')
def delete_user(user, target_user):
    return f"删除用户 {target_user}"

# 测试
admin_user = {'name': 'admin', 'permissions': ['admin', 'edit']}
normal_user = {'name': 'user', 'permissions': ['read']}

print(delete_user(admin_user, 'test'))  # 成功
# delete_user(normal_user, 'test')  # 抛出权限错误

4.4 重试装饰器

处理网络请求等不稳定操作时,重试机制非常有用:

Python
import time
from functools import wraps

def retry(max_attempts=3, delay=1):
    def decorator(func):
        @wraps(func)
        def wrapper(*args, **kwargs):
            for attempt in range(max_attempts):
                try:
                    return func(*args, **kwargs)
                except Exception as e:
                    if attempt == max_attempts - 1:
                        raise
                    print(f"第 {attempt + 1} 次失败,{delay}秒后重试:{e}")
                    time.sleep(delay)
        return wrapper
    return decorator

@retry(max_attempts=3, delay=2)
def unstable_operation():
    import random
    if random.random() < 0.7:
        raise ConnectionError("网络连接失败")
    return "操作成功"

4.5 日志记录装饰器

统一的日志记录对于系统维护至关重要:

Python
import logging
from functools import wraps

logging.basicConfig(level=logging.INFO)

def log_decorator(logger=None):
    def decorator(func):
        @wraps(func)
        def wrapper(*args, **kwargs):
            log_msg = f"调用 {func.__name__}, 参数:args={args}, kwargs={kwargs}"
            if logger:
                logger.info(log_msg)
            else:
                logging.info(log_msg)
            
            try:
                result = func(*args, **kwargs)
                logging.info(f"{func.__name__} 返回:{result}")
                return result
            except Exception as e:
                logging.error(f"{func.__name__} 异常:{e}")
                raise
        return wrapper
    return decorator

@log_decorator()
def divide(a, b):
    return a / b

divide(10, 2)

五、装饰器栈与执行顺序

多个装饰器可以堆叠使用,执行顺序是从下往上(靠近函数的先执行),返回时从上往下:

Python
def decorator_a(func):
    @wraps(func)
    def wrapper(*args, **kwargs):
        print("A: 进入")
        result = func(*args, **kwargs)
        print("A: 离开")
        return result
    return wrapper

def decorator_b(func):
    @wraps(func)
    def wrapper(*args, **kwargs):
        print("B: 进入")
        result = func(*args, **kwargs)
        print("B: 离开")
        return result
    return wrapper

@decorator_a
@decorator_b
def test():
    print("函数执行")
    return "结果"

test()
# 输出顺序:
# B: 进入
# 函数执行
# B: 离开
# A: 进入
# A: 离开

六、最佳实践与注意事项

6.1 使用 functools.wraps

始终使用 INLINE_CODE_6 保留原函数的元数据,这对于调试和文档生成非常重要。

6.2 处理可变参数

确保装饰器能够正确处理 INLINE_CODE_7INLINE_CODE_8,以适配各种函数签名。

6.3 避免副作用

装饰器应该尽可能保持"纯净",避免产生意外的副作用。

6.4 性能考虑

装饰器会增加函数调用的开销,对于性能敏感的代码路径,需要权衡使用。

结语

装饰器是 Python 中最强大的特性之一,它让我们能够以优雅的方式实现代码复用和功能扩展。从简单的日志记录到复杂的权限控制,装饰器的应用场景无处不在。

掌握装饰器的关键在于理解其本质——函数包装函数。一旦理解了这一点,你就能够灵活运用装饰器来解决各种实际问题。希望本文能够帮助你深入理解装饰器,并在实际项目中发挥其最大价值。

记住,好的代码不仅是能运行的代码,更是易于理解、易于维护的代码。装饰器正是帮助我们写出这样代码的有力工具。

分享到:

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

加载评论中...