什么是Python装饰器?

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

装饰器的核心概念

装饰器的工作原理基于Python的几个重要特性:

  • 函数是一等公民:函数可以作为参数传递,也可以作为返回值
  • 闭包:内部函数可以访问外部函数的变量
  • 语法糖@decorator 语法提供了简洁的装饰器应用方式

基础装饰器实现

1. 最简单的装饰器

def my_decorator(func):
    def wrapper():
        print("函数执行前")
        func()
        print("函数执行后")
    return wrapper

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

# 调用
say_hello()

输出:

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

2. 带参数的函数装饰器

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

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

# 调用
add(3, 5)

输出:

调用函数: add
参数: (3, 5), {}
返回值: 8

装饰器的高级用法

1. 带参数的装饰器

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

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

greet("Alice")

输出:

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

2. 类装饰器

class CountCalls:
    def __init__(self, func):
        self.func = func
        self.calls = 0
    
    def __call__(self, *args, **kwargs):
        self.calls += 1
        print(f"函数被调用了 {self.calls} 次")
        return self.func(*args, **kwargs)

@CountCalls
def say_whee():
    print("Whee!")

say_whee()
say_whee()

输出:

函数被调用了 1 次
Whee!
函数被调用了 2 次
Whee!

3. 保留原函数元信息

from functools import wraps

def preserve_info(func):
    @wraps(func)
    def wrapper(*args, **kwargs):
        return func(*args, **kwargs)
    return wrapper

@preserve_info
def example():
    """这是一个示例函数"""
    pass

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

实际应用场景

1. 性能监控装饰器

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()

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"用户 {target_user} 已被删除")

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

delete_user(admin_user, "Charlie")  # 成功
# delete_user(regular_user, "Charlie")  # 抛出异常

3. 缓存装饰器

def memoize(func):
    cache = {}
    @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)

print(fibonacci(10))  # 快速计算,避免重复计算

4. 日志记录装饰器

import logging

logging.basicConfig(level=logging.INFO)

def log_execution(func):
    @wraps(func)
    def wrapper(*args, **kwargs):
        logging.info(f"开始执行 {func.__name__}")
        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 risky_operation():
    # 可能出错的操作
    return 10 / 0

装饰器的组合使用

def make_bold(func):
    @wraps(func)
    def wrapper():
        return f"<b>{func()}</b>"
    return wrapper

def make_italic(func):
    @wraps(func)
    def wrapper():
        return f"<i>{func()}</i>"
    return wrapper

@make_bold
@make_italic
def greet():
    return "Hello, World!"

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

内置装饰器

Python提供了一些内置装饰器:

1. @property

class Circle:
    def __init__(self, radius):
        self._radius = radius
    
    @property
    def radius(self):
        return self._radius
    
    @radius.setter
    def radius(self, value):
        if value <= 0:
            raise ValueError("半径必须为正数")
        self._radius = value
    
    @property
    def area(self):
        return 3.14159 * self._radius ** 2

c = Circle(5)
print(c.radius)  # 5
c.radius = 10
print(c.area)    # 314.159

2. @classmethod 和 @staticmethod

class MyClass:
    @classmethod
    def from_string(cls, string):
        return cls(string)
    
    @staticmethod
    def is_valid(string):
        return len(string) > 0

obj = MyClass.from_string("test")
print(MyClass.is_valid("test"))  # True

3. @functools.lru_cache

from functools import lru_cache

@lru_cache(maxsize=128)
def expensive_function(n):
    # 模拟耗时计算
    return n * n

# 第一次调用会计算
print(expensive_function(5))
# 第二次调用直接返回缓存结果
print(expensive_function(5))

装饰器的最佳实践

  1. 始终使用@wraps:保留原函数的元信息
  2. 保持装饰器简单:每个装饰器只做一件事
  3. 考虑性能:装饰器会增加函数调用开销
  4. 文档化:清楚地说明装饰器的作用
  5. 测试:确保装饰器不会破坏原有功能

总结

Python装饰器是一个强大而灵活的工具,它可以:

  • 增强函数功能而不修改源代码
  • 保持代码的DRY原则(Don’t Repeat Yourself)
  • 提高代码的可读性和可维护性
  • 实现关注点分离

通过合理使用装饰器,你可以编写出更加优雅、高效的Python代码。从简单的日志记录到复杂的权限控制,装饰器都能提供优雅的解决方案。