Python装饰器
在Python中,装饰器 Decorator 是一种用于修改函数或者方法的强大工具。下面以统计方法执行时间为例,来介绍Python装饰器的基本用法。
1 基本装饰器实现
以下是一个简单的装饰器示例,用于统计方法的执行时间:
import time
def timing_decorator(func):
def wrapper(*args, **kwargs):
start_time = time.time()
result = func(*args, **kwargs)
end_time = time.time()
print(f"Execution time: {end_time - start_time} seconds")
return result
return wrapper
# 使用装饰器
@timing_decorator
def my_function():
time.sleep(1)
print("Function executed")
# 调用被装饰的函数
my_function()
使用 functools.wraps 保留原函数信息
在上述示例中,装饰器会修改原函数的元信息,例如函数名、文档字符串等。为了保留原函数的元信息,可以使用 functools.wraps 装饰器:
import time
import functools import wraps
def timing_decorator(func):
@wraps(func)
def wrapper(*args, **kwargs):
start_time = time.time()
result = func(*args, **kwargs)
end_time = time.time()
print(f"Execution time: {end_time - start_time} seconds")
return result
return wrapper
@timing_decorator
def my_function():
"""This is my function"""
time.sleep(1)
print("Function executed")
print(my_function.__name__) # 输出: my_function
print(my_function.__doc__) # 输出: This is my function
3 带参数的装饰器
装饰器也可以接受参数,例如下面的示例中,可以指定是否打印函数执行时间:
import time
from functools import wraps
def timing_decorator_with_params(format_string="{func_name} took {time:.4f} seconds"):
def decorator(func):
@wraps(func)
def wrapper(*args, **kwargs):
start_time = time.time()
result = func(*args, **kwargs)
end_time = time.time()
print(format_string.format(func_name=func.__name__, time=end_time - start_time))
return result
return wrapper
return decorator
@timing_decorator_with_params(format_string="{func_name} executed in {time:.6f} seconds")
def my_function():
time.sleep(2)
print("Function executed.")
# 调用被装饰的函数
my_function()
4 类方法中使用装饰器
装饰器也可以用于类方法,例如下面的示例中,用于统计类方法的执行时间:
import time
from functools import wraps
def timing_decorator(func):
@wraps(func)
def wrapper(*args, **kwargs):
start_time = time.time()
result = func(*args, **kwargs)
end_time = time.time()
execution_time = end_time - start_time
print(f"Method '{func.__name__}' took {execution_time:.4f} seconds to execute.")
return result
return wrapper
class MyClass:
@timing_decorator
def my_method(self):
time.sleep(2)
print("Method executed.")
# 创建类实例并调用方法
obj = MyClass()
obj.my_method()
5 使用contextlib实现上下文管理器
除了使用装饰器,还可以使用 contextlib 模块中的 contextmanager 装饰器来实现上下文管理器,例如下面的示例中,用于统计代码块的执行时间:
import time
from contextlib import contextmanager
@contextmanager
def timing_context():
start_time = time.time()
yield
end_time = time.time()
execution_time = end_time - start_time
print(f"Code block took {execution_time:.4f} seconds to execute.")
# 使用上下文管理器
with timing_context():
time.sleep(2)
print("Code block executed.")
装饰器用于实例方法的注意事项
在使用装饰器装饰实例方法时,需要注意装饰器的参数列表中是否包含 self 参数。如果装饰器的参数列表中不包含 self 参数,那么在装饰实例方法时,需要在装饰器内部手动传入 self 参数。
- self 参数:实例方法的第一个参数是 self,表示实例对象本身。装饰器在调用被装饰方法时,需要确保传递 self 参数。
- 方法绑定:装饰器返回的函数或方法需要正确绑定到实例对象。Python 会自动处理这一点,但如果你手动调用装饰器返回的函数,需要确保传递正确的参数。
总结
- 装饰器:通过装饰器可以方便地统计函数的执行时间,而无需修改函数本身的代码。
- functools.wraps:用于保留被装饰函数的元信息。
- 参数化装饰器:可以通过参数化装饰器来定制输出格式或其他行为。
- 类方法装饰器:装饰器也可以用于类方法。
- 上下文管理器:使用 contextlib 可以统计代码块的执行时间。