Python 装饰器:从入门到精通

一、简介

在 Python 编程中,装饰器(Decorator)是一种强大的语法糖,它允许开发者在不修改原函数代码的情况下,动态增强函数功能。这种设计模式广泛应用于日志记录、权限验证、性能测试和缓存等场景,是 Python 元编程的重要组成部分。本文将从基础概念出发,逐步深入探讨装饰器的各种应用技巧。

二、装饰器基础原理

2.1 函数作为一等公民

在 Python 中,函数是一等公民(First-Class Citizen),这意味着它们可以:

  • 作为变量赋值
  • 作为参数传递
  • 作为返回值返回
  • 存储在数据结构中

以下示例展示了函数作为变量和参数的用法:

 

def greet(name):
    return f"Hello, {name}!"

# 将函数赋值给变量
hello = greet

# 函数作为参数传递
def call_function(func, arg):
    return func(arg)

print(call_function(hello, "Alice"))  # 输出: Hello, Alice!

2.2 闭包(Closure)

闭包是装饰器的核心机制,它指的是一个函数可以访问并记住其外部作用域中的变量,即使外部函数已经执行完毕。

 

def outer(x):
    def inner(y):
        return x + y  # 闭包捕获了外部变量x
    return inner

add_five = outer(5)
print(add_five(3))  # 输出: 8

2.3 简单装饰器实现

装饰器本质上是一个高阶函数,它接收一个函数作为输入,并返回一个新的函数。以下是一个计算函数执行时间的装饰器示例:

 

import time

def timer_decorator(func):
    def wrapper(*args, **kwargs):
        start_time = time.time()
        result = func(*args, **kwargs)  # 执行原函数
        end_time = time.time()
        print(f"函数 {func.__name__} 执行耗时: {end_time - start_time:.4f} 秒")
        return result
    return wrapper

@timer_decorator
def calculate_sum(n):
    return sum(range(n+1))

print(calculate_sum(1000000))
# 输出:
# 函数 calculate_sum 执行耗时: 0.0520 秒
# 500000500000

 

这个装饰器的工作原理如下:

 

  1. timer_decorator 接收 calculate_sum 函数作为参数
  2. 内部定义了 wrapper 函数来包裹原函数
  3. 返回 wrapper 函数替代原函数
  4. @timer_decorator 语法糖等价于 calculate_sum = timer_decorator(calculate_sum)

三、装饰器进阶用法

3.1 带参数的装饰器

有时我们需要为装饰器传递额外参数,这可以通过创建一个三层嵌套函数实现:

 

def repeat(n):
    def decorator(func):
        def wrapper(*args, **kwargs):
            result = None
            for _ in range(n):
                result = func(*args, **kwargs)
            return result
        return wrapper
    return decorator

@repeat(3)
def say_hi():
    print("Hi!")

say_hi()  # 输出三次 Hi!

3.2 类装饰器

除了函数装饰器,Python 还支持使用类作为装饰器。类装饰器通过实现 __call__ 方法来实现:

 

class CountCalls:
    def __init__(self, func):
        self.func = func
        self.num_calls = 0

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

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

say_hello()  # 输出: 第 1 次调用 say_hello \n Hello!
say_hello()  # 输出: 第 2 次调用 say_hello \n Hello!

3.3 保留函数元信息

当使用装饰器时,原函数的元信息(如 __name____doc__)会被覆盖。可以使用 functools.wraps 来保留这些信息:

 

import functools

def debug(func):
    @functools.wraps(func)  # 保留原函数元信息
    def wrapper(*args, **kwargs):
        print(f"调用 {func.__name__}({args}, {kwargs})")
        result = func(*args, **kwargs)
        print(f"{func.__name__} 返回 {result}")
        return result
    return wrapper

@debug
def add(a, b):
    """返回两数之和"""
    return a + b

print(add(3, 5))
print(add.__name__)  # 输出: add (而不是 wrapper)
print(add.__doc__)   # 输出: 返回两数之和

3.4 多个装饰器叠加

多个装饰器可以叠加使用,它们会按照从下到上的顺序依次应用:

 

def make_bold(func):
    def wrapper(*args, **kwargs):
        return f"<b>{func(*args, **kwargs)}</b>"
    return wrapper

def make_italic(func):
    def wrapper(*args, **kwargs):
        return f"<i>{func(*args, **kwargs)}</i>"
    return wrapper

@make_bold
@make_italic
def greet(name):
    return f"Hello, {name}!"

print(greet("Alice"))  # 输出: <b><i>Hello, Alice!</i></b>

四、实战应用案例

4.1 日志记录

自动记录函数调用和返回值:

 

def log_decorator(func):
    def wrapper(*args, **kwargs):
        result = func(*args, **kwargs)
        with open('app.log', 'a') as f:
            f.write(f"{func.__name__}({args}, {kwargs}) -> {result}\n")
        return result
    return wrapper

@log_decorator
def calculate(a, b):
    return a * b

4.2 权限验证

在执行敏感操作前验证用户权限:

 

def requires_admin(func):
    def wrapper(user, *args, **kwargs):
        if user.get('role') != 'admin':
            raise PermissionError("需要管理员权限")
        return func(user, *args, **kwargs)
    return wrapper

@requires_admin
def delete_user(user, username):
    print(f"删除用户 {username}")

4.3 缓存机制

使用 functools.lru_cache 缓存函数结果:

 

import functools

@functools.lru_cache(maxsize=128)
def fibonacci(n):
    if n <= 1:
        return n
    return fibonacci(n-1) + fibonacci(n-2)

print(fibonacci(50))  # 第一次计算较慢,后续调用将直接使用缓存结果

4.4 性能测试

统计函数执行时间并生成报告:

 

import time
import functools

def performance_report(func):
    @functools.wraps(func)
    def wrapper(*args, **kwargs):
        start_time = time.perf_counter()
        result = func(*args, **kwargs)
        end_time = time.perf_counter()
        print(f"{func.__name__} 执行时间: {end_time - start_time:.6f} 秒")
        return result
    return wrapper

@performance_report
def process_data(data):
    # 模拟数据处理
    time.sleep(1)
    return len(data)

五、装饰器高级话题

5.1 装饰器类的参数化

结合类装饰器和参数化装饰器:

 

class Retry:
    def __init__(self, max_attempts=3):
        self.max_attempts = max_attempts

    def __call__(self, func):
        def wrapper(*args, **kwargs):
            attempts = 0
            while attempts < self.max_attempts:
                try:
                    return func(*args, **kwargs)
                except Exception as e:
                    attempts += 1
                    print(f"尝试 {attempts}/{self.max_attempts} 失败: {e}")
            raise Exception("达到最大重试次数")
        return wrapper

@Retry(max_attempts=5)
def connect_to_database():
    # 模拟数据库连接,可能会失败
    import random
    if random.random() < 0.8:
        raise ConnectionError("连接失败")
    return "连接成功"

5.2 装饰器与异步函数

异步函数的装饰器需要使用 async defawait

 

import asyncio
import time

def async_timer(func):
    async def wrapper(*args, **kwargs):
        start_time = time.time()
        result = await func(*args, **kwargs)
        end_time = time.time()
        print(f"异步函数 {func.__name__} 执行耗时: {end_time - start_time:.4f} 秒")
        return result
    return wrapper

@async_timer
async def fetch_data(url):
    await asyncio.sleep(1)  # 模拟网络请求
    return {"data": "example"}

5.3 装饰器与类方法

装饰器也可以应用于类中的方法:

 

def debug_method(func):
    def wrapper(self, *args, **kwargs):
        print(f"调用方法 {func.__name__} 来自实例 {self}")
        return func(self, *args, **kwargs)
    return wrapper

class MyClass:
    @debug_method
    def method(self, x):
        return x * 2

六、总结

装饰器是 Python 中一种非常灵活且强大的工具,它允许我们在不修改原有代码的情况下增强函数或类的功能。通过理解装饰器的基本原理(函数作为一等公民和闭包),我们可以创建出各种实用的装饰器,用于日志记录、权限验证、缓存、性能测试等场景。

在实际应用中,我们需要注意以下几点:

  1. 使用 functools.wraps 保留原函数元信息
  2. 理解装饰器的执行顺序(从下到上)
  3. 合理设计带参数的装饰器
  4. 注意异步函数装饰器的特殊性

掌握装饰器是 Python 程序员进阶的关键一步,它不仅能让你的代码更加简洁优雅,还能提高代码的复用性和可维护性。希望通过本文的介绍,你对 Python 装饰器有了更深入的理解和掌握。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值