Python装饰器教学
一、装饰器简介
在Python中,装饰器是一种高级语法特性,允许你修改或增强函数、方法或类的行为,而无需修改其源代码。装饰器本质上是一个接受函数作为参数的函数,并返回一个新的函数对象。
二、装饰器的基本用法
下面是一个简单的装饰器示例:
def my_decorator(func):def wrapper():print("Before function call")func()print("After function call")return wrapper@my_decorator
def say_hello():print("Hello!")say_hello()
输出:
Before function call
Hello!
After function call
在这个例子中,my_decorator
是一个装饰器函数,它接受一个函数 func
作为参数,并返回一个新的函数 wrapper
。wrapper
函数在调用原始函数 func
之前和之后分别打印一些内容。
通过使用 @my_decorator
语法,我们可以将装饰器应用到任何函数上,如 say_hello
函数。这实际上是将 say_hello
函数作为参数传递给 my_decorator
函数,并将返回的新函数对象重新赋值给 say_hello
。
三、带参数的装饰器
如果原始函数需要接受参数,我们可以在 wrapper
函数中添加相应的参数,如下所示:
def my_decorator(func):def wrapper(name):print(f"Before calling {func.__name__}")func(name)print(f"After calling {func.__name__}")return wrapper@my_decorator
def greet(name):print(f"Hello, {name}!")greet("Alice")
输出:
Before calling greet
Hello, Alice!
After calling greet
在这个例子中,greet
函数接受一个参数 name
,并通过装饰器 my_decorator
增强了其行为。注意,在 wrapper
函数中,我们使用 func.__name__
来获取原始函数的名称。
四、带参数的装饰器工厂
有时,我们可能希望装饰器本身能够接受参数。这可以通过创建一个返回装饰器的函数来实现,如下所示:
def my_decorator_factory(message):def my_decorator(func):def wrapper(name):print(f"{message} Before calling {func.__name__}")func(name)print(f"{message} After calling {func.__name__}")return wrapperreturn my_decorator@my_decorator_factory("This is a custom message: ")
def greet(name):print(f"Hello, {name}!")greet("Bob")
输出:
This is a custom message: Before calling greet
Hello, Bob!
This is a custom message: After calling greet
在这个例子中,my_decorator_factory
是一个返回装饰器的函数。它接受一个参数 message
,并返回一个装饰器函数 my_decorator
。这样,我们就可以通过传递不同的参数来创建具有不同行为的装饰器。
五、装饰器的应用场景
装饰器在Python编程中有许多应用场景,例如:
- 日志记录:记录函数调用的时间、参数和返回值等信息。
- 权限校验:在函数调用之前检查用户权限。
- 缓存实现:缓存函数调用的结果,避免重复计算。
- 性能统计:测量函数执行的时间并打印性能报告。
- 事务处理:确保数据库操作的原子性、一致性和隔离性。
- 测试与调试:在开发过程中添加额外的调试信息或测试逻辑。
六、日志记录示例
日志记录是装饰器的一个非常实用的应用场景。下面是一个使用装饰器来实现日志记录的示例:
import logging
from functools import wraps# 配置日志
logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')# 定义一个日志装饰器
def log_decorator(func):@wraps(func) # 使用 functools.wraps 来保留原始函数的元信息(如函数名、文档字符串等)def wrapper(*args, **kwargs):# 在函数调用前记录日志logging.info(f"Calling function {func.__name__} with args {args} and kwargs {kwargs}")# 调用原始函数并获取结果result = func(*args, **kwargs)# 在函数调用后记录日志logging.info(f"Function {func.__name__} returned {result}")# 返回函数结果return resultreturn wrapper# 使用装饰器来增强函数
@log_decorator
def add_numbers(a, b):"""Adds two numbers and returns the result."""return a + b# 调用增强后的函数
result = add_numbers(5, 3)
print(result)
在这个例子中,我们首先导入了 logging
模块和 functools.wraps
装饰器。logging
模块用于记录日志,而 wraps
装饰器用于保留原始函数的元信息,这样在日志中就可以正确地显示函数名。
我们定义了一个名为 log_decorator
的装饰器,它接受一个函数作为参数,并返回一个新的函数 wrapper
。在 wrapper
函数内部,我们在调用原始函数前后分别记录了日志。
然后,我们使用 @log_decorator
语法将装饰器应用到 add_numbers
函数上。这样,每当我们调用 add_numbers
函数时,就会自动记录相应的日志。
运行这段代码,你将看到类似以下的输出:
2023-04-25 10:00:00,000 - INFO - Calling function add_numbers with args (5, 3) and kwargs {}
2023-04-25 10:00:00,001 - INFO - Function add_numbers returned 8
8
这里的日志输出显示了函数调用前后的信息,包括函数名、参数和返回值。这对于调试和监控程序的运行非常有用。