Python中的装饰器
装饰器是Python中一个非常重要且强大的概念,它可以在不修改原函数代码的情况下,动态地扩展函数的功能。在Python中,函数本身也是对象,因此我们可以像操作普通对象一样来操作函数,比如赋值给变量、作为参数传递等。装饰器实际上就是一个闭包函数,它接受一个函数作为参数,并返回一个新的函数。当我们在定义一个函数时,在函数的上方添加 @decorator
这样的语法糖,就能够使用装饰器来对函数进行装饰。
装饰器的基本用法
下面我们来看一个简单的装饰器的示例:
def hello_decorator(func):
def wrapper():
print("Before function execution")
func()
print("After function execution")
return wrapper
@hello_decorator
def say_hello():
print("Hello, world!")
say_hello()
运行上面的代码,输出如下:
Before function execution
Hello, world!
After function execution
在这个示例中,我们定义了一个装饰器函数 hello_decorator
,它接受一个函数作为参数,并返回一个新的函数 wrapper
。在 wrapper
函数中,我们首先打印一段信息,然后调用原始函数,最后再打印一段信息。在调用 say_hello
函数时,实际上是调用了被装饰后的函数。
带参数的装饰器
有时候我们需要给装饰器传递参数,例如我们想指定打印的信息。下面是一个带参数的装饰器的示例:
def decorator_with_args(message):
def real_decorator(func):
def wrapper():
print(f"Before function execution: {message}")
func()
print(f"After function execution: {message}")
return wrapper
return real_decorator
@decorator_with_args("Decoration message")
def say_hello():
print("Hello, world!")
say_hello()
运行上面的代码,输出如下:
Before function execution: Decoration message
Hello, world!
After function execution: Decoration message
在这个示例中,我们定义了一个带参数的装饰器 decorator_with_args
,它接受一个参数 message
,并返回一个装饰器函数 real_decorator
。在 real_decorator
函数中,我们可以访问传递进来的参数,并在 wrapper
函数中使用。然后可以像普通装饰器一样使用这个带参数的装饰器。
类装饰器
除了函数装饰器外,Python还支持类装饰器。类装饰器是一个类,它必须实现 __call__
方法,并在这个方法中实现装饰器的逻辑。下面是一个类装饰器的示例:
class DecoratorClass:
def __init__(self, func):
self.func = func
def __call__(self):
print("Before function execution")
self.func()
print("After function execution")
@DecoratorClass
def say_hello():
print("Hello, world!")
say_hello()
运行上面的代码,输出如下:
Before function execution
Hello, world!
After function execution
在这个示例中,我们定义了一个类 DecoratorClass
,它接受一个函数作为参数,并在 __call__
方法中实现装饰器的逻辑。然后将函数 say_hello
使用类装饰器 DecoratorClass
进行装饰。
多个装饰器
在Python中,一个函数可以被多个装饰器装饰。装饰器的执行顺序是从下往上执行,即最后装饰的最先执行。下面是一个使用多个装饰器的示例:
def decorator1(func):
def wrapper():
print("Decorator 1 - Before function execution")
func()
print("Decorator 1 - After function execution")
return wrapper
def decorator2(func):
def wrapper():
print("Decorator 2 - Before function execution")
func()
print("Decorator 2 - After function execution")
return wrapper
@decorator1
@decorator2
def say_hello():
print("Hello, world!")
say_hello()
运行上面的代码,输出如下:
Decorator 1 - Before function execution
Decorator 2 - Before function execution
Hello, world!
Decorator 2 - After function execution
Decorator 1 - After function execution
在这个示例中,我们定义了两个装饰器 decorator1
和 decorator2
,然后将它们分别装饰在 say_hello
函数上。由于装饰器的执行顺序是从下往上执行,所以 decorator2
先执行,然后是 decorator1
。
带参数的类装饰器
类装饰器也可以带参数,只需要在类初始化时接受参数即可。下面是一个带参数的类装饰器的示例:
class DecoratorClassWithArgs:
def __init__(self, message):
self.message = message
def __call__(self, func):
def wrapper():
print(f"Before function execution: {self.message}")
func()
print(f"After function execution: {self.message}")
return wrapper
@DecoratorClassWithArgs("Decoration message")
def say_hello():
print("Hello, world!")
say_hello()
运行上面的代码,输出如下:
Before function execution: Decoration message
Hello, world!
After function execution: Decoration message
在这个示例中,我们定义了一个带参数的类装饰器 DecoratorClassWithArgs
,在初始化时接受参数 message
。然后在 __call__
方法中实现装饰器的逻辑。最后将函数 say_hello
使用带参数的类装饰器装饰。
functools.wraps装饰器
在定义装饰器时,如果不加 functools.wraps
装饰器,原函数的元信息(如函数名、文档字符串)会丢失。为了保留原函数的元信息,我们可以在内部函数中使用 functools.wraps
装饰器。下面是一个示例:
import functools
def my_decorator(func):
@functools.wraps(func)
def wrapper():
"""This is a wrapper function"""
print("Before function execution")
func()
print("After function execution")
return wrapper
@my_decorator
def say_hello():
"""This is the original function"""
print("Hello, world!")
say_hello()
print(say_hello.__name__)
print(say_hello.__doc__)
运行上面的代码,输出如下:
Before function execution
Hello, world!
After function execution
say_hello
This is the original function
在这个示例中,我们在 wrapper
函数中使用 functools.wraps(func)
装饰器来保留原函数 say_hello
的元信息。这样做可以确保装饰器不会影响原函数的属性。
带参数的装饰器
有时候我们需要给装饰器传递参数,并且这些参数可能会影响装饰器的行为。下面是一个带参数的装饰器的示例:
def parametrized_decorator(param1, param2):
def real_decorator(func):
def wrapper():
print(f"Decorator parameters: {param1}, {param2}")
func()
return wrapper
return real_decorator
@parametrized_decorator("Param1", "Param2")
def say_hello():
print("Hello, world!")
say_hello()
运行上面的代码,输出如下:
Decorator parameters: Param1, Param2
Hello, world!
在这个示例中,我们定义了一个带参数的装饰器 parametrized_decorator
,它接受两个参数,并返回一个装饰器函数 real_decorator
。在 real_decorator
函数中,我们可以访问传递进来的参数,并在 wrapper
函数中使用。
装饰器的应用场景
装饰器在Python中有着广泛的应用场景,比如日志记录、性能测试、缓存、权限验证等。下面是一个使用装饰器记录函数执行时间的示例:
import time
def performance_decorator(func):
def wrapper(*args, **kwargs):
start_time = time.time()
result = func(*args, **kwargs)
end_time = time.time()
print(f"Function {func.__name__} executed in {(end_time - start_time):.2f} seconds")
return result
return wrapper
@performance_decorator
def time_consuming_function():
time.sleep(2)
print("Function executed!")
time_consuming_function()
运行上面的代码,输出如下:
Function executed!
Function time_consuming_function executed in 2.00 seconds
在这个示例中,我们定义了一个装饰器 performance_decorator
,它记录了函数执行的时间。将 time_consuming_function
使用装饰器装饰后,可以方便地获取函数执行的时间。
总结
装饰器是Python中非常强大和灵活的功能,可以用来动态地扩展函数的功能,而不需要改变原函数的代码。通过装饰器,我们可以实现很多功能,比如日志记录、性能测试、权限验证等。掌握装饰器的使用方法可以让我们写出更加优雅和灵活的代码。