Python装饰器高级模式:从日志到AOP的完整实现
Python装饰器高级模式:从日志到AOP的完整实现
作者:Crown_22 | AI Agent & Hermes Agent 桌面程序开发者
前言
装饰器是Python中最优雅的特性之一,但大多数开发者只停留在@property和@staticmethod的基础用法。实际上,装饰器可以实现日志、缓存、重试、权限控制、性能监控等众多高级功能。
本文将从装饰器的原理出发,逐步深入到高级模式,最终实现一个完整的AOP(面向切面编程)框架。
一、装饰器基础回顾
1.1 最简单的装饰器
defmy_decorator(func):defwrapper(*args,**kwargs):print("Before call")result=func(*args,**kwargs)print("After call")returnresultreturnwrapper@my_decoratordefsay_hello():print("Hello!")say_hello()# 输出:# Before call# Hello!# After call1.2 带参数的装饰器
defrepeat(n):defdecorator(func):defwrapper(*args,**kwargs):for_inrange(n):result=func(*args,**kwargs)returnresultreturnwrapperreturndecorator@repeat(3)defgreet(name):print(f"Hello,{name}!")greet("World")# 输出:# Hello, World!# Hello, World!# Hello, World!1.3 类装饰器
classLogger:def__init__(self,func):self.func=funcdef__call__(self,*args,**kwargs):print(f"Calling{self.func.__name__}")result=self.func(*args,**kwargs)print(f"Finished{self.func.__name__}")returnresult@Loggerdefadd(a,b):returna+bprint(add(1,2))# 输出:# Calling add# Finished add# 3二、保留原函数信息
2.1 问题:装饰器丢失元信息
defmy_decorator(func):defwrapper(*args,**kwargs):returnfunc(*args,**kwargs)returnwrapper@my_decoratordefexample():"""这是文档字符串"""passprint(example.__name__)# 输出: wrapper(错误!)print(example.__doc__)# 输出: None(错误!)2.2 解决方案:使用functools.wraps
fromfunctoolsimportwrapsdefmy_decorator(func):@wraps(func)defwrapper(*args,**kwargs):returnfunc(*args,**kwargs)returnwrapper@my_decoratordefexample():"""这是文档字符串"""passprint(example.__name__)# 输出: example(正确!)print(example.__doc__)# 输出: 这是文档字符串(正确!)2.3 最佳实践:始终使用@wraps
fromfunctoolsimportwrapsfromtypingimportAny,Callable,TypeVar F=TypeVar('F',bound=Callable[...,Any])defmy_decorator(func:F)->F:@wraps(func)defwrapper(*args:Any,**kwargs:Any)->Any:returnfunc(*args,**kwargs)returnwrapper# type: ignore三、实用装饰器模式
3.1 日志装饰器
基础版本:
fromfunctoolsimportwrapsimportlogging logging.basicConfig(level=logging.INFO)logger=logging.getLogger(__name__)deflog_calls(func):@wraps(func)defwrapper(*args,**kwargs):logger.info(f"Calling{func.__name__}with args={args}, kwargs={kwargs}")try:result=func(*args,**kwargs)logger.info(f"{func.__name__}returned{result}")returnresultexceptExceptionase:logger.error(f"{func.__name__}raised{type(e).__name__}:{e}")raisereturnwrapper@log_callsdefadd(a:int,b:int)->int:returna+b add(1,2)# 输出:# INFO:__main__:Calling add with args=(1, 2), kwargs={}# INFO:__main__:add returned 3高级版本:支持自定义日志级别和格式
fromfunctoolsimportwrapsfromtypingimportOptional,Callableimportloggingimporttimedeflog_calls(level:int=logging.INFO,message:Optional[str]=None,log_args:bool=True,log_result:bool=True,log_time:bool=False):"""高级日志装饰器 Args: level: 日志级别 message: 自定义消息模板 log_args: 是否记录参数 log_result: 是否记录返回值 log_time: 是否记录执行时间 """defdecorator(func:Callable)->Callable:@wraps(func)defwrapper(*args,**kwargs):logger=logging.getLogger(func.__module__)# 构建日志消息msg=messageorf"Calling{func.__name__}"iflog_args:msg+=f" with args={args}, kwargs={kwargs}"logger.log(level,msg)# 记录执行时间start_time=time.time()iflog_timeelseNonetry:result=func(*args,**kwargs)iflog_result:logger.log(level,f"{func.__name__}returned{result}")iflog_time:elapsed=time.time()-start_time logger.log(level,f"{func.__name__}took{elapsed:.4f}s")returnresultexceptExceptionase:logger.error(f"{func.__name__}raised{type(e).__name__