python装饰器
在Python中,装饰器(Decorator)是一种设计模式,它允许用户在不修改原有函数或类结构的情况下,动态地添加功能。装饰器本质上是一个函数(或类),它接受一个函数(或类)作为参数,并返回一个新的函数(或类)。这个新的函数通常会在执行原函数的基础上增加一些额外的操作。
装饰器通常用于以下场景:
- 日志记录
- 性能测试(如计算运行时间)
- 事务处理
- 权限校验
- 缓存等
常见装饰器
1.统计函数运行耗时
def timer_decorator(func):def wrapper(*args, **kwargs):import timestart = time.pref_time()result = func(*args, **kwargs) # 执行原函数end = time.pref_time()print(f"{func.__name__} 耗时 {end-start:.4f}秒")return resultreturn wrapper@timer_decorator
def heavy_calculation(n):return sum(i*i for i in range(n))heavy_calculation(10**6)
# 输出:heavy_calculation 耗时 0.1253秒
2.多次执行函数
def repeat(num_times):def decorator(func):def wrapper(*args, **kwargs):for _ in range(num_times):func(*args, **kwargs)return wrapperreturn decorator@repeat(num_times=3)
def say_hello(name):print(f"Hello, {name}!")say_hello("Alice")
# 输出:
# Hello, Alice!
# Hello, Alice!
# Hello, Alice!
3.计算函数调用次数
class CountCalls:def __init__(self, func):self.func = funcself.calls = 0def __call__(self, *args, **kwargs):self.calls += 1print(f"已调用 {self.calls} 次")return self.func(*args, **kwargs)@CountCalls
def example():print("执行示例函数")example() # 输出:已调用 1 次 → 执行示例函数
example() # 输出:已调用 2 次 → 执行示例函数
元信息(Metadata)
在Python中,每个函数都有一些内置的属性,比如函数名(`__name__`)、文档字符串(`__doc__`)等,这些信息被称为**元信息(metadata)**。当我们使用装饰器时,实际上是用一个新的函数(通常称为`wrapper`)替换了原始函数。如果不做特殊处理,原始函数的这些元信息就会被`wrapper`函数的元信息所覆盖,这可能会导致一些问题,例如:
1. 原始函数的函数名(`__name__`)会变成`wrapper`,而不是原来的名字。
2. 原始函数的文档字符串(`__doc__`)会丢失。
3. 其他属性(比如模块名`__module__`、参数列表`__annotations__`等)也会被覆盖。
关键元信息包括:
元信息属性 | 描述 | 示例 |
---|---|---|
__name__ | 函数名称 | func.__name__ → "add" |
__doc__ | 函数的文档字符串(docstring) | func.__doc__ |
__module__ | 函数所属的模块名 | func.__module__ |
__annotations__ | 函数的类型注解 | func.__annotations__ |
__qualname__ | 函数的限定名(含类名) | Class.func.__qualname__ |
保留元信息的装饰器
from functools import wrapsdef good_decorator(func):@wraps(func) # 关键:复制元信息到包装函数def wrapper(*args, **kwargs):return func(*args, **kwargs)return wrapper@good_decorator
def add(a: int, b: int) -> int:"""两个数相加"""return a + b# 元信息被正确保留:
print(add.__name__) # 输出:add
print(add.__doc__) # 输出:"两个数相加"
print(add.__annotations__) # 输出:{'a': <class 'int'>, ...}