什么是装饰器?

装饰器(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!"

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

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

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

2. 闭包的概念

装饰器利用了Python的闭包特性。闭包是指一个函数可以访问它被定义时的环境(包括局部变量)。

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 slow_function():
    """模拟耗时操作"""
    time.sleep(1)
    return "完成"

result = slow_function()
print(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"你好, {name}!")

greet("小明")

输出:

你好, 小明!
你好, 小明!
你好, 小明!

高级装饰器技术

1. 使用functools.wraps

当我们使用装饰器时,原函数的元信息(如函数名、文档字符串)会被包装函数覆盖。使用functools.wraps可以保留原函数的元信息:

import functools

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

@logging_decorator
def example_function():
    """这是一个示例函数"""
    pass

print(example_function.__name__)  # 输出: example_function
print(example_function.__doc__)   # 输出: 这是一个示例函数

2. 类装饰器

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

class CountCalls:
    """统计函数调用次数的类装饰器"""
    def __init__(self, func):
        self.func = func
        self.call_count = 0
    
    def __call__(self, *args, **kwargs):
        self.call_count += 1
        print(f"函数 {self.func.__name__} 被调用了 {self.call_count} 次")
        return self.func(*args, **kwargs)

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

say_hello()
say_hello()
say_hello()

3. 装饰器堆叠(多层装饰器)

Python允许多个装饰器堆叠使用,它们的执行顺序是从下到上:

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
import functools

logging.basicConfig(level=logging.INFO)

def log_execution(func):
    @functools.wraps(func)
    def wrapper(*args, **kwargs):
        logging.info(f"开始执行: {func.__name__},参数: {args}, {kwargs}")
        try:
            result = func(*args, **kwargs)
            logging.info(f"函数 {func.__name__} 执行成功")
            return result
        except Exception as e:
            logging.error(f"函数 {func.__name__} 执行失败: {e}")
            raise
    return wrapper

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

# 测试
try:
    print(divide(10, 2))
    print(divide(10, 0))
except:
    pass

2. 缓存/记忆化

import functools
import time

def memoize(func):
    """缓存函数结果的装饰器"""
    cache = {}
    
    @functools.wraps(func)
    def wrapper(*args):
        if args in cache:
            return cache[args]
        result = func(*args)
        cache[args] = result
        return result
    return wrapper

@memoize
def fibonacci(n):
    """计算斐波那契数列"""
    if n < 2:
        return n
    return fibonacci(n-1) + fibonacci(n-2)

# 测试性能
start = time.time()
print(fibonacci(30))
print(f"耗时: {time.time() - start:.4f} 秒")

3. 权限验证

def require_admin(func):
    """需要管理员权限的装饰器"""
    @functools.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'}

try:
    delete_user(admin_user, 'Charlie')  # 成功
    delete_user(regular_user, 'Charlie')  # 失败
except PermissionError as e:
    print(f"错误: {e}")

4. 性能监控

import time
import functools

def performance_monitor(func):
    """监控函数性能的装饰器"""
    @functools.wraps(func)
    def wrapper(*args, **kwargs):
        start = time.perf_counter()
        result = func(*args, **kwargs)
        end = time.perf_counter()
        execution_time = end - start
        
        if execution_time > 1.0:
            logging.warning(f"函数 {func.__name__} 执行缓慢: {execution_time:.4f} 秒")
        
        return result
    return wrapper

@performance_monitor
def heavy_computation():
    time.sleep(1.5)
    return "计算完成"

print(heavy_computation())

装饰器的参数和返回值处理

1. 通用装饰器模板

import functools

def universal_decorator(func):
    @functools.wraps(func)
    def wrapper(*args, **kwargs):
        # 前置处理
        print(f"准备调用 {func.__name__}")
        
        # 调用原函数
        result = func(*args, **kwargs)
        
        # 后置处理
        print(f"{func.__name__} 返回结果: {result}")
        
        return result
    return wrapper

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

print(add(3, 5))

2. 处理特殊场景

def validate_input(*validations):
    """参数验证装饰器"""
    def decorator(func):
        @functools.wraps(func)
        def wrapper(*args, **kwargs):
            # 验证位置参数
            for i, (arg, validation) in enumerate(zip(args, validations)):
                if not validation(arg):
                    raise ValueError(f"参数 {i} 验证失败: {arg}")
            
            # 验证关键字参数
            for key, value in kwargs.items():
                if key in validations and not validations[key](value):
                    raise ValueError(f"关键字参数 {key} 验证失败: {value}")
            
            return func(*args, **kwargs)
        return wrapper
    return decorator

# 使用
@validate_input(lambda x: x > 0, lambda y: y > 0)
def divide_positive(a, b):
    return a / b

print(divide_positive(10, 2))  # 成功
# print(divide_positive(-1, 2))  # 会抛出ValueError

装饰器的调试技巧

1. 使用namedoc检查

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

@debug_decorator
def test_func(x):
    """测试函数文档"""
    return x * 2

print(f"函数名: {test_func.__name__}")
print(f"文档: {test_func.__doc__}")

2. 使用inspect模块检查签名

import inspect

def inspect_decorator(func):
    @functools.wraps(func)
    def wrapper(*args, **kwargs):
        sig = inspect.signature(func)
        bound = sig.bind(*args, **kwargs)
        bound.apply_defaults()
        print(f"调用 {func.__name__},参数: {dict(bound.arguments)}")
        return func(*args, **kwargs)
    return wrapper

@inspect_decorator
def example(a, b=2, c=3):
    return a + b + c

example(1, c=4)

常见陷阱和解决方案

1. 装饰器不保留原函数元信息

问题:

def bad_decorator(func):
    def wrapper():
        return func()
    return wrapper

@bad_decorator
def test():
    """测试文档"""
    pass

print(test.__name__)  # 输出: wrapper
print(test.__doc__)   # 输出: None

解决方案:

import functools

def good_decorator(func):
    @functools.wraps(func)
    def wrapper():
        return func()
    return wrapper

@good_decorator
def test():
    """测试文档"""
    pass

print(test.__name__)  # 输出: test
print(test.__doc__)   # 输出: 测试文档

2. 装饰器参数传递错误

# 错误示例
def wrong_decorator(func):
    def wrapper(*args, **kwargs):
        print("装饰器参数:", func)  # 这里会打印函数对象,而不是参数
        return func(*args, **kwargs)
    return wrapper

@wrong_decorator("参数1", "参数2")
def example():
    pass

正确做法:

def correct_decorator(*dec_args, **dec_kwargs):
    def decorator(func):
        @functools.wraps(func)
        def wrapper(*args, **kwargs):
            print("装饰器参数:", dec_args, dec_kwargs)
            return func(*args, **kwargs)
        return wrapper
    return decorator

@correct_decorator("参数1", "参数2", key="value")
def example():
    pass

example()

装饰器的最佳实践

1. 保持装饰器的单一职责

# 不好的做法 - 一个装饰器做太多事
def messy_decorator(func):
    @functools.wraps(func)
    def wrapper(*args, **kwargs):
        # 日志
        print(f"调用 {func.__name__}")
        # 权限检查
        if not check_permission():
            raise PermissionError()
        # 性能监控
        start = time.time()
        result = func(*args, **kwargs)
        print(f"耗时: {time.time() - start}")
        # 缓存
        cache[args] = result
        return result
    return wrapper

# 好的做法 - 分离关注点
def log_decorator(func):
    @functools.wraps(func)
    def wrapper(*args, **kwargs):
        print(f"调用 {func.__name__}")
        return func(*args, **kwargs)
    return wrapper

def permission_decorator(func):
    @functools.wraps(func)
    def wrapper(*args, **kwargs):
        if not check_permission():
            raise PermissionError()
        return func(*args, **kwargs)
    return wrapper

def performance_decorator(func):
    @functools.wraps(func)
    def wrapper(*args, **kwargs):
        start = time.time()
        result = func(*args, **kwargs)
        print(f"耗时: {time.time() - start}")
        return result
    return wrapper

# 组合使用
@log_decorator
@permission_decorator
@performance_decorator
def clean_function():
    pass

2. 为装饰器添加配置选项

def configurable_decorator(
    enable_logging=True,
    enable_timing=False,
    log_level="INFO"
):
    def decorator(func):
        @functools.wraps(func)
        def wrapper(*args, **kwargs):
            if enable_logging:
                print(f"[{log_level}] 调用 {func.__name__}")
            
            if enable_timing:
                start = time.time()
                result = func(*args, **kwargs)
                print(f"耗时: {time.time() - start}")
                return result
            else:
                return func(*args, **kwargs)
        
        return wrapper
    return decorator

# 使用
@configurable_decorator(enable_logging=True, enable_timing=True, log_level="DEBUG")
def my_function():
    time.sleep(0.1)
    return "完成"

my_function()

装饰器的高级应用:元类装饰器

1. 类装饰器的高级用法

class Singleton:
    """单例模式的类装饰器"""
    def __init__(self, cls):
        self.cls = cls
        self.instance = None
    
    def __call__(self, *args, **kwargs):
        if self.instance is None:
            self.instance = self.cls(*args, **kwargs)
        return self.instance
    
    def __getattr__(self, name):
        return getattr(self.cls, name)

@Singleton
class DatabaseConnection:
    def __init__(self):
        print("创建数据库连接...")
        self.connected = True

# 测试
db1 = DatabaseConnection()
db2 = DatabaseConnection()
print(f"是否是同一个实例: {db1 is db2}")  # True

2. 使用装饰器实现策略模式

class Strategy:
    """策略模式装饰器"""
    def __init__(self, name):
        self.name = name
        self.strategies = {}
    
    def register(self, func):
        self.strategies[func.__name__] = func
        return func
    
    def execute(self, strategy_name, *args, **kwargs):
        if strategy_name not in self.strategies:
            raise ValueError(f"未知策略: {strategy_name}")
        return self.strategies[strategy_name](*args, **kwargs)

# 使用
payment_strategy = Strategy("Payment")

@payment_strategy.register
def credit_card(amount):
    return f"信用卡支付: {amount}元"

@payment_strategy.register
def alipay(amount):
    return f"支付宝支付: {amount}元"

print(payment_strategy.execute("credit_card", 100))
print(payment_strategy.execute("alipay", 200))

装饰器的性能考虑

1. 装饰器的性能开销

import time

def measure_overhead(func):
    @functools.wraps(func)
    def wrapper(*args, **kwargs):
        return func(*args, **kwargs)
    return wrapper

def raw_function(n):
    return sum(range(n))

def decorated_function(n):
    return sum(range(n))

# 测试性能差异
n = 1000000

# 原始函数
start = time.time()
raw_function(n)
raw_time = time.time() - start

# 装饰后的函数
decorated = measure_overhead(decorated_function)
start = time.time()
decorated(n)
decorated_time = time.time() - start

print(f"原始函数耗时: {raw_time:.6f}秒")
print(f"装饰后函数耗时: {decorated_time:.6f}秒")
print(f"开销: {(decorated_time - raw_time) / raw_time * 100:.2f}%")

2. 优化装饰器性能

# 使用functools.lru_cache优化递归装饰器
import functools

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

# 更好的方式:直接使用functools.lru_cache
@functools.lru_cache(maxsize=128)
def fibonacci_optimized(n):
    if n < 2:
        return n
    return fibonacci_optimized(n-1) + fibonacci_optimized(n-2)

# 测试
print(fibonacci_optimized(30))
print(f"缓存信息: {fibonacci_optimized.cache_info()}")

装饰器的测试策略

1. 测试装饰器本身

import unittest
from unittest.mock import patch

def test_logging_decorator():
    """测试日志装饰器"""
    with patch('builtins.print') as mock_print:
        @log_execution
        def test_func():
            return "result"
        
        result = test_func()
        
        # 验证打印了开始和结束信息
        assert mock_print.call_count == 2
        assert "开始执行" in mock_print.call_args_list[0][0][0]
        assert "执行成功" in mock_print.call_args_list[1][0][0]
        assert result == "result"

# 运行测试
test_logging_decorator()
print("装饰器测试通过!")

2. 测试被装饰的函数

def test_decorated_function():
    """测试被装饰的函数行为"""
    
    @memoize
    def add(a, b):
        return a + b
    
    # 第一次调用
    result1 = add(1, 2)
    assert result1 == 3
    
    # 第二次调用相同参数,应该从缓存返回
    result2 = add(1, 2)
    assert result2 == 3
    
    # 不同参数
    result3 = add(2, 3)
    assert result3 == 5
    
    print("所有测试通过!")

test_decorated_function()

总结

装饰器是Python中非常强大和灵活的特性,掌握它们可以让你的代码更加优雅和可维护。记住以下要点:

  1. 理解基础:装饰器本质上是高阶函数,利用了闭包特性
  2. 使用functools.wraps:保留原函数的元信息
  3. 处理参数:正确处理位置参数和关键字参数
  4. 组合使用:可以堆叠多个装饰器
  5. 保持简洁:每个装饰器应该只做一件事
  6. 考虑性能:注意装饰器的性能开销
  7. 充分测试:测试装饰器本身和被装饰的函数

通过实践这些概念和技巧,你将能够创建出功能强大、易于维护的装饰器,显著提升你的Python编程技能。