面试题答案
一键面试可能的性能瓶颈分析
- 函数调用开销:每次函数调用都要经过装饰器,额外的函数调用会增加开销。例如,装饰器函数的执行本身就有函数调用、参数传递等操作。
- 日志记录开销:如果日志记录涉及磁盘I/O操作,比如写入文件,频繁的I/O操作会严重影响性能。而且格式化日志信息也需要消耗一定的CPU资源。
优化方案
- 减少函数调用开销:
- 使用functools.wraps:它可以保留被装饰函数的元数据,同时在装饰器内部可以直接调用被装饰函数,减少不必要的间接调用。
import functools def log_decorator(func): @functools.wraps(func) def wrapper(*args, **kwargs): # 日志记录代码 result = func(*args, **kwargs) return result return wrapper @log_decorator def my_function(): pass
- 内联装饰器逻辑:对于简单的装饰器逻辑,可以直接将装饰器逻辑写在函数内部,避免额外的函数调用。
def my_function(): # 日志记录代码 pass
- 优化日志记录开销:
- 异步日志记录:使用
asyncio
库将日志记录操作异步化,避免阻塞主程序。
import asyncio import logging async def log_async(message): await asyncio.sleep(0) # 模拟异步操作 logging.info(message) def async_log_decorator(func): async def wrapper(*args, **kwargs): await log_async(f"Calling {func.__name__}") result = await func(*args, **kwargs) await log_async(f"{func.__name__} finished") return result return wrapper @async_log_decorator async def my_async_function(): pass
- 批量日志记录:可以将日志信息先缓存起来,然后定期批量写入磁盘。
- 异步日志记录:使用
通过元编程技巧优化灵活性
- 根据运行环境动态改变装饰器行为:可以通过检查环境变量来决定装饰器的行为。
import os def conditional_log_decorator(func): if os.environ.get('DEBUG') == 'true': def wrapper(*args, **kwargs): print(f"DEBUG: Calling {func.__name__}") result = func(*args, **kwargs) print(f"DEBUG: {func.__name__} finished") return result else: def wrapper(*args, **kwargs): result = func(*args, **kwargs) return result return wrapper @conditional_log_decorator def my_function(): pass
- 通过传递参数改变装饰器行为:
def log_level_decorator(level): def decorator(func): def wrapper(*args, **kwargs): if level == 'debug': print(f"DEBUG: Calling {func.__name__}") elif level == 'info': print(f"INFO: Calling {func.__name__}") result = func(*args, **kwargs) if level == 'debug': print(f"DEBUG: {func.__name__} finished") elif level == 'info': print(f"INFO: {func.__name__} finished") return result return wrapper return decorator @log_level_decorator('debug') def my_function(): pass