什么是装饰器?

装饰器(Decorator)是Python中一种强大的语法特性,它允许你在不修改原有函数代码的情况下,为函数添加额外的功能。装饰器本质上是一个高阶函数,它接收一个函数作为参数,并返回一个新的函数。

装饰器的基本概念

想象一下,你有一个函数,你想在不改变这个函数本身的前提下,给它添加一些额外的功能,比如日志记录、性能测试、权限校验等。装饰器就是为了解决这类问题而生的。

# 最简单的装饰器示例
def my_decorator(func):
    def wrapper():
        print("函数执行前")
        func()
        print("函数执行后")
    return wrapper

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

# 调用函数
say_hello()

输出结果:

函数执行前
Hello!
函数执行后

装饰器的工作原理

1. 函数作为一等公民

在Python中,函数是一等公民,这意味着函数可以:

  • 被赋值给变量
  • 作为参数传递给其他函数
  • 作为其他函数的返回值
def greet():
    return "Hello!"

# 函数赋值给变量
say_hello = greet
print(say_hello())  # 输出: Hello!

# 函数作为参数
def call_function(func):
    return func()

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

2. 闭包的概念

装饰器依赖于闭包的概念。闭包是指一个函数可以访问其外部作用域的变量,即使外部函数已经执行完毕。

def outer_function(msg):
    message = msg
    
    def inner_function():
        print(message)
    
    return inner_function

my_func = outer_function("Hello, World!")
my_func()  # 输出: Hello, World!

基础装饰器实现

1. 不带参数的函数装饰器

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):
    total = 0
    for i in range(n):
        total += i
    return total

# 测试
result = calculate_sum(1000000)
print(f"计算结果: {result}")

2. 带参数的函数装饰器

如果需要装饰器本身接受参数,需要多一层包装:

def repeat_decorator(times):
    def decorator(func):
        def wrapper(*args, **kwargs):
            for _ in range(times):
                result = func(*args, **kwargs)
            return result
        return wrapper
    return decorator

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

greet("Alice")

输出:

Hello, Alice!
Hello, Alice!
Hello, Alice!

高级装饰器技术

1. 使用functools.wraps

当我们使用装饰器时,原函数的元信息(如函数名、文档字符串等)会被包装函数覆盖。为了解决这个问题,可以使用functools.wraps

import functools

def debug_decorator(func):
    @functools.wraps(func)
    def wrapper(*args, **kwargs):
        print(f"调用函数: {func.__name__}")
        print(f"参数: args={args}, kwargs={kwargs}")
        result = func(*args, **kwargs)
        print(f"返回值: {result}")
        return result
    return wrapper

@debug_decorator
def add(a, b):
    """计算两个数的和"""
    return a + b

print(add.__name__)  # 输出: add (而不是wrapper)
print(add.__doc__)   # 输出: 计算两个数的和

2. 类装饰器

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

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} 次")
        return self.func(*args, **kwargs)

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

say_hello()
say_hello()
say_hello()

输出:

函数被调用了 1 次
Hello!
函数被调用了 2 次
Hello!
函数被调用了 3 次
Hello!

3. 多个装饰器的组合

多个装饰器可以堆叠使用,执行顺序是从下到上:

def bold_decorator(func):
    def wrapper():
        return f"<b>{func()}</b>"
    return wrapper

def italic_decorator(func):
    def wrapper():
        return f"<i>{func()}</i>"
    return wrapper

@bold_decorator
@italic_decorator
def hello():
    return "Hello!"

print(hello())  # 输出: <b><i>Hello!</i></b>

实际应用场景

1. 日志记录

import logging
from functools import wraps

def log_execution(func):
    @wraps(func)
    def wrapper(*args, **kwargs):
        logging.basicConfig(level=logging.INFO)
        logger = logging.getLogger(func.__name__)
        logger.info(f"开始执行 {func.__name__}")
        try:
            result = func(*args, **kwargs)
            logger.info(f"{func.__name__} 执行成功")
            return result
        except Exception as e:
            logger.error(f"{func.__name__} 执行失败: {str(e)}")
            raise
    return wrapper

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

# 测试
try:
    result = divide(10, 2)
    print(f"结果: {result}")
    divide(10, 0)
except:
    pass

2. 权限验证

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

@require_admin
def delete_user(user, target_user):
    print(f"用户 {user['name']} 删除了用户 {target_user}")

# 测试
admin_user = {'name': 'Alice', 'role': 'admin'}
regular_user = {'name': 'Bob', 'role': 'user'}

delete_user(admin_user, "Charlie")  # 成功
try:
    delete_user(regular_user, "Charlie")  # 失败
except PermissionError as e:
    print(e)

3. 缓存/记忆化

import time
from functools import wraps

def cache(seconds):
    def decorator(func):
        cache_dict = {}
        
        @wraps(func)
        def wrapper(*args):
            current_time = time.time()
            
            # 清理过期的缓存
            expired_keys = [k for k, v in cache_dict.items() if current_time - v['time'] > seconds]
            for k in expired_keys:
                del cache_dict[k]
            
            # 检查是否有缓存
            if args in cache_dict:
                print(f"使用缓存结果: {args}")
                return cache_dict[args]['result']
            
            # 执行函数并缓存结果
            result = func(*args)
            cache_dict[args] = {'result': result, 'time': current_time}
            print(f"计算新结果: {args}")
            return result
        
        return wrapper
    return decorator

@cache(5)  # 缓存5秒
def expensive_calculation(n):
    time.sleep(1)  # 模拟耗时计算
    return n * n

# 测试
print(expensive_calculation(5))
print(expensive_calculation(5))
time.sleep(6)  # 等待缓存过期
print(expensive_calculation(5))

4. 性能监控

import time
from functools import wraps
import functools

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

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

# 测试
print(f"Fibonacci(30) = {fibonacci(30)}")

装饰器的高级用法

1. 带参数的类装饰器

class ValidateInput:
    def __init__(self, min_value=None, max_value=None):
        self.min_value = min_value
        self.max_value = max_value
    
    def __call__(self, func):
        @functools.wraps(func)
        def wrapper(*args, **kwargs):
            for arg in args:
                if self.min_value is not None and arg < self.min_value:
                    raise ValueError(f"参数 {arg} 小于最小值 {self.min_value}")
                if self.max_value is not None and arg > self.max_value:
                    raise ValueError(f"参数 {arg} 大于最大值 {self.max_value}")
            return func(*args, **kwargs)
        return wrapper

@ValidateInput(min_value=0, max_value=100)
def set_age(age):
    print(f"设置年龄为: {age}")

set_age(25)  # 正常
try:
    set_age(150)  # 异常
except ValueError as e:
    print(e)

2. 装饰器保留原函数调用方式

class CallableDecorator:
    def __init__(self, func):
        self.func = func
        functools.update_wrapper(self, func)
    
    def __call__(self, *args, **kwargs):
        print(f"装饰器处理: {self.func.__name__}")
        return self.func(*args, **kwargs)
    
    def __get__(self, instance, owner):
        # 支持类方法
        if instance is None:
            return self
        return functools.partial(self.__call__, instance)

@CallableDecorator
def my_function(x, y):
    return x + y

# 作为普通函数调用
print(my_function(3, 4))

# 作为方法调用
class MyClass:
    @CallableDecorator
    def method(self, x):
        return x * 2

obj = MyClass()
print(obj.method(5))

装饰器的调试技巧

1. 使用inspect模块检查装饰器

import inspect

def debug_decorator(func):
    def wrapper(*args, **kwargs):
        print(f"函数签名: {inspect.signature(func)}")
        print(f"参数: args={args}, kwargs={kwargs}")
        result = func(*args, **kwargs)
        print(f"返回值: {result}")
        return result
    return wrapper

@debug_decorator
def example(a, b, c=10):
    return a + b + c

example(1, 2, c=3)

2. 装饰器的单元测试

import unittest

def test_decorator():
    def my_decorator(func):
        def wrapper(*args, **kwargs):
            return func(*args, **kwargs) * 2
        return wrapper
    
    @my_decorator
    def add(a, b):
        return a + b
    
    assert add(1, 2) == 6  # (1+2)*2 = 6
    print("测试通过!")

test_decorator()

装饰器的性能考虑

1. 装饰器的性能开销

import time

def no_decorator():
    total = 0
    for i in range(1000000):
        total += i
    return total

def with_decorator():
    def dummy_decorator(func):
        def wrapper(*args, **kwargs):
            return func(*args, **kwargs)
        return wrapper
    
    @dummy_decorator
    def inner():
        total = 0
        for i in range(1000000):
            total += i
        return total
    
    return inner()

# 性能比较
start = time.time()
no_decorator()
print(f"无装饰器: {time.time() - start:.4f}秒")

start = time.time()
with_decorator()
print(f"有装饰器: {time.time() - start:.4f}秒")

2. 优化装饰器性能

# 使用__slots__减少内存占用
class OptimizedDecorator:
    __slots__ = ('func',)
    
    def __init__(self, func):
        self.func = func
        functools.update_wrapper(self, func)
    
    def __call__(self, *args, **kwargs):
        return self.func(*args, **kwargs)

装饰器的最佳实践

1. 保持装饰器简单

每个装饰器应该只做一件事。如果需要多个功能,使用多个装饰器。

# 好的做法
@log_execution
@validate_input
@performance_monitor
def complex_function():
    pass

# 不好的做法
@all_in_one_decorator  # 一个装饰器做太多事情
def complex_function():
    pass

2. 文档化装饰器

def rate_limit(max_calls, period):
    """
    限制函数在指定时间内的调用次数
    
    Args:
        max_calls (int): 最大调用次数
        period (int): 时间周期(秒)
    
    Returns:
        function: 装饰器函数
    
    Example:
        @rate_limit(5, 60)  # 每分钟最多5次调用
        def api_call():
            pass
    """
    def decorator(func):
        call_history = []
        
        @functools.wraps(func)
        def wrapper(*args, **kwargs):
            current_time = time.time()
            
            # 清理过期记录
            call_history[:] = [t for t in call_history if current_time - t < period]
            
            if len(call_history) >= max_calls:
                raise Exception("调用频率过高")
            
            call_history.append(current_time)
            return func(*args, **kwargs)
        
        return wrapper
    return decorator

总结

装饰器是Python中一个非常强大且灵活的特性,它允许我们以声明式的方式为函数添加行为。通过本文的详细讲解和丰富的示例,你应该已经掌握了:

  1. 基础概念:理解装饰器的工作原理和闭包的概念
  2. 基本语法:学会如何创建和使用简单的装饰器
  3. 高级技巧:掌握带参数的装饰器、类装饰器、多个装饰器组合等
  4. 实际应用:了解装饰器在日志记录、权限验证、缓存、性能监控等场景的应用
  5. 最佳实践:知道如何编写可维护、高性能的装饰器

装饰器是Python编程中不可或缺的工具,熟练掌握它们将大大提升你的代码质量和开发效率。记住,好的装饰器应该简单、专注、文档完善,并且考虑到性能影响。

在实际项目中,装饰器可以帮助你实现关注点分离,使业务逻辑更加清晰,代码更加模块化和可重用。继续练习和探索,你会发现更多装饰器的妙用!