# Python装饰器详解:让你的代码更优雅

装饰器是Python中一种强大的语法糖,可以在不修改原函数的情况下,增强函数的功能。本文详细介绍装饰器的原理和使用方法,让你的Python代码更加优雅和高效。

# 一、装饰器基础

# 什么是装饰器

# 装饰器本质是一个函数,接收函数作为参数,返回增强后的函数

def my_decorator(func):
    """装饰器函数"""
    def wrapper(*args, **kwargs):
        print("函数执行前...")
        result = func(*args, **kwargs)
        print("函数执行后...")
        return result
    return wrapper

# 使用装饰器
@my_decorator
def say_hello(name):
    print(f"你好,{name}!")

# 等价于
# say_hello = my_decorator(say_hello)

# 调用
say_hello("程序员晚枫")

输出:

函数执行前...
你好,程序员晚枫!
函数执行后...

# 带参数的装饰器

def log_decorator(prefix="LOG"):
    """带参数的装饰器工厂"""
    def decorator(func):
        def wrapper(*args, **kwargs):
            print(f"[{prefix}] 开始执行: {func.__name__}")
            result = func(*args, **kwargs)
            print(f"[{prefix}] 执行完成: {func.__name__}")
            return result
        return wrapper
    return decorator

@log_decorator(prefix="INFO")
def add(a, b):
    return a + b

@log_decorator(prefix="DEBUG")
def multiply(a, b):
    return a * b

# 使用
print(add(10, 20))
print(multiply(10, 20))

# 二、装饰器高级用法

# 保留原函数元信息

from functools import wraps

def my_decorator(func):
    @wraps(func)  # 保留原函数的元信息
    def wrapper(*args, **kwargs):
        """这是wrapper的文档"""
        print("执行前")
        result = func(*args, **kwargs)
        print("执行后")
        return result
    return wrapper

@my_decorator
def original_function():
    """这是原始函数的文档"""
    print("执行中")

# 测试元信息
print(f"函数名: {original_function.__name__}")
print(f"文档: {original_function.__doc__}")

# 类装饰器

class CallCounter:
    """统计函数调用次数的装饰器"""
    def __init__(self, func):
        self.func = func
        self.count = 0
        wraps(func)(self)  # 保留元信息
    
    def __call__(self, *args, **kwargs):
        self.count += 1
        print(f"函数 {self.func.__name__} 被调用了 {self.count} 次")
        return self.func(*args, **kwargs)

@CallCounter
def fetch_data():
    return "数据"

# 使用
fetch_data()  # 调用1次
fetch_data()  # 调用2次
fetch_data()  # 调用3次
print(f"总计调用: {fetch_data.count} 次")

# 多装饰器叠加

def decorator1(func):
    @wraps(func)
    def wrapper(*args, **kwargs):
        print("decorator1 执行")
        return func(*args, **kwargs)
    return wrapper

def decorator2(func):
    @wraps(func)
    def wrapper(*args, **kwargs):
        print("decorator2 执行")
        return func(*args, **kwargs)
    return wrapper

# 装饰器从下到上执行
@decorator1
@decorator2
def test():
    print("test 执行")

# 等价于: decorator1(decorator2(test()))

test()
# 输出顺序:
# decorator1 执行
# decorator2 执行  
# test 执行

# 三、常用装饰器示例

# 计时装饰器

import time
from functools import wraps

def timer(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

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

@timer
def calculate_sum(n):
    return sum(range(n))

# 使用
slow_function()
calculate_sum(1000000)

# 日志装饰器

import logging
from functools import wraps

def log_calls(logger=None):
    """记录函数调用日志"""
    if logger is None:
        logger = logging.getLogger(__name__)
    
    def decorator(func):
        @wraps(func)
        def wrapper(*args, **kwargs):
            args_repr = [repr(a) for a in args]
            kwargs_repr = [f"{k}={v!r}" for k, v in kwargs.items()]
            signature = ", ".join(args_repr + kwargs_repr)
            
            logger.info(f"调用 {func.__name__}({signature})")
            try:
                result = func(*args, **kwargs)
                logger.info(f"{func.__name__} 返回: {result!r}")
                return result
            except Exception as e:
                logger.exception(f"{func.__name__} 发生异常: {e}")
                raise
        return wrapper
    return decorator

# 使用
@log_calls()
def divide(a, b):
    return a / b

divide(10, 2)
divide(10, 0)

# 缓存装饰器

from functools import lru_cache
import time

# 使用lru_cache实现缓存
@lru_cache(maxsize=128)
def expensive_calculation(n):
    """耗时的计算"""
    time.sleep(1)  # 模拟耗时操作
    return n * n

print("第一次调用:")
start = time.time()
print(f"结果: {expensive_calculation(100)}")
print(f"耗时: {time.time() - start:.4f}s")

print("\n第二次调用(缓存):")
start = time.time()
print(f"结果: {expensive_calculation(100)}")
print(f"耗时: {time.time() - start:.4f}s")

# 重试装饰器

import time
from functools import wraps

def retry(max_attempts=3, delay=1, backoff=2, exceptions=(Exception,)):
    """重试装饰器"""
    def decorator(func):
        @wraps(func)
        def wrapper(*args, **kwargs):
            attempts = 0
            current_delay = delay
            
            while attempts < max_attempts:
                try:
                    return func(*args, **kwargs)
                except exceptions as e:
                    attempts += 1
                    if attempts >= max_attempts:
                        print(f"达到最大重试次数 {max_attempts},放弃")
                        raise
                    
                    print(f"第 {attempts} 次尝试失败,{current_delay}秒后重试... 错误: {e}")
                    time.sleep(current_delay)
                    current_delay *= backoff  # 指数退避
            
            return None
        return wrapper
    return decorator

@retry(max_attempts=3, delay=1, exceptions=(ConnectionError,))
def fetch_data():
    import random
    if random.random() < 0.7:
        raise ConnectionError("网络不稳定")
    return "数据获取成功"

# 测试
try:
    result = fetch_data()
    print(f"最终结果: {result}")
except Exception as e:
    print(f"最终失败: {e}")

# 四、类中的装饰器

# 方法装饰器

class DataProcessor:
    def __init__(self):
        self.data = []
    
    def timer(method):
        """方法计时装饰器"""
        @wraps(method)
        def wrapper(self, *args, **kwargs):
            start = time.time()
            result = method(self, *args, **kwargs)
            print(f"{method.__name__} 执行耗时: {time.time() - start:.4f}s")
            return result
        return wrapper
    
    @timer
    def load_data(self, path):
        time.sleep(0.5)
        self.data = [1, 2, 3, 4, 5]
        return len(self.data)
    
    @timer
    def process_data(self):
        time.sleep(0.3)
        return [x * 2 for x in self.data]

# 使用
processor = DataProcessor()
processor.load_data('data.csv')
processor.process_data()

# 属性装饰器

class User:
    def __init__(self, name, age):
        self._name = name
        self._age = age
    
    @property
    def name(self):
        """姓名属性(只读)"""
        return self._name
    
    @property
    def age(self):
        """年龄属性,带验证"""
        return self._age
    
    @age.setter
    def age(self, value):
        if value < 0:
            raise ValueError("年龄不能为负数")
        self._age = value

# 使用
user = User("张三", 25)
print(f"姓名: {user.name}")
print(f"年龄: {user.age}")

user.age = 30
print(f"修改后的年龄: {user.age}")

# 五、装饰器应用场景

装饰器类型 应用场景 示例
计时器 性能分析 @timer
日志 记录调用 @log_calls
缓存 加速重复计算 @lru_cache
重试 网络请求 @retry
权限 访问控制 @requires_auth
验证 参数检查 @validate_params
节流 限制调用频率 @throttle

# 相关资源

# 总结

装饰器是Python最强大的特性之一。掌握装饰器的原理和使用方法,可以让代码更加模块化、可复用,提升开发效率。多多练习,将装饰器应用到实际项目中去吧!

Last Updated: 4/23/2026, 9:22:28 AM