什么是装饰器
装饰器(decorators)是 一种设计模式,之所以被称为装饰器,是因为其本质是对一个函数或者对象进行了"修饰",装饰器可以在你进入对象或者函数前后进行一些操作,可以在不修改原来的代码的情况下让函数的行为发生改变。
面向切面编程(AOP),是指某些业务流程具有相似性,只有部分核心代码有差异,我们可以把相同部分的代码提取出来,不同的代码(切片)另外实现,这就是面向切面编程。在 java 的 spring 中,这是一种很重要的技术,而 python 也有类似的实现方式,最常见的就是装饰器。
装饰器的功能
装饰器允许你动态地修改函数或类的行为。它本质上是一个函数,可以接受一个函数作为参数,并返回一个新的函数或者修改原来的函数。
装饰器语法
装饰器虽然是一个函数,但它更常见的使用方式是使用 @decorator_name
来应用在函数或方法上。
例如:
from flask import Flask
app =Flask(__name__)
# app.route就是一个Flask内置的装饰器
@app.route("index")
def index():
return "hello"
Python 还提供了一些内置的装饰器,比如 @staticmethod
和 @classmethod
,用于定义静态方法和类方法。
装饰器的应用场景:
- 日志记录: 装饰器可用于记录函数的调用信息、参数和返回值。
- 性能分析: 可以使用装饰器来测量函数的执行时间。
- 权限控制: 装饰器可用于限制对某些函数的访问权限。
- 缓存: 装饰器可用于实现函数结果的缓存,以提高性能。
简单案例
import time
def runtime(func):#定义一个装饰器函数叫runtime 他接受一个函数func作为参数
def wrapper(*args, **kwargs): # 包装函数wrapper,这里接受所有参数
# 这里是在调用原始函数前添加的新功能
starttime= time.time() #获取开始执行的时间
# 在包装函数中调用原始函数(可以不调用)
result = func(*args, **kwargs) # 函数传递的参数为包装函数接受的所有参数
# 这里是在调用原始函数后添加的新功能
endtime= time.time() #获取结束执行的时间
print(f"函数{func.__name__}运行时间:{endtime-starttime}秒") #输出函数运行的时间
return result # 返回函数运行的结果
return wrapper
# 使用装饰器装饰一个函数,现在运行这个函数时,会运行装饰器的代码,然后再由装饰器运行这个func
# (如果装饰器不调用这个函数,而是调用别的函数,就可以替换掉原有函数的功能)
@runtime
def sleepfunc(arg1, arg2):
time.sleep(2)
return arg1 + arg2
print(sleepfunc(1, 2))
函数sleepfunc运行时间:2.005006790161133秒
3
解析:runtime
是一个装饰器函数,它接受一个函数 func
作为参数,并返回一个内部函数 wrapper
,在 wrapper
函数内部,你可以执行一些额外的操作,然后调用原始函数 func
,并返回其结果。
- runtime 是装饰器,它接收一个函数 func 作为参数。
- wrapper 是内部函数,它是实际会被调用的新函数,它包裹了原始函数的调用,并在其前后增加了额外的行为。
- 当我们使用
@runtime
前缀在 sleepfunc
定义前,Python会自动将 sleepfunc
作为参数传递给 runtime
,然后将返回的 wrapper
函数替换掉原来的 sleepfunc
。
使用装饰器
装饰器通过 @
符号应用在函数定义之前,例如:
@time_logger
def target_function():
pass
等同于:
def target_function():
pass
target_function = time_logger(target_function)
这会将 target_function
函数传递给 decorator
装饰器,并将返回的函数重新赋值给 target_function
。从而,每次调用 target_function
时,实际上是调用了经过装饰器处理后的函数。
通过装饰器,开发者可以在保持代码整洁的同时,灵活且高效地扩展程序的功能。
带参数的装饰器
装饰器函数也可以接受参数,但是需要在原有的装饰器外再套一层装饰器。
例如:
实例
import time
def runtime(n:int):#定义一个装饰装饰器的装饰函数,他可以接受参数
def decorator(func):#定义一个装饰器函数,接受一个函数func作为参数
def wrapper(*args, **kwargs): # 包装函数wrapper,这里接受所有参数
# 这里是在调用原始函数前添加的新功能
starttime= time.time() #获取开始执行的时间
# 在包装函数中调用原始函数(可以不调用)
result = func(*args, **kwargs) # 函数传递的参数为包装函数接受的所有参数
# 这里是在调用原始函数后添加的新功能
endtime= time.time() #获取结束执行的时间
print(f"函数{func.__name__}运行时间:{endtime-starttime}秒") #输出函数运行的时间
print("装饰器传递过来的参数为:",n)
return result # 返回函数运行的结果
return wrapper # 返回包装函数
return decorator # 返回装饰器函数
# 使用装饰器装饰一个函数,现在运行这个函数时,会运行装饰器的代码,然后再由装饰器运行这个func
# (如果装饰器不调用这个函数,而是调用别的函数,就可以替换掉原有函数的功能)
@runtime(1)
def sleepfunc(arg1, arg2):
time.sleep(2)
return arg1 + arg2
print(sleepfunc(1, 2))
运行结果为:
函数sleepfunc运行时间:2.000138521194458秒
装饰器传递过来的参数为: 1
3
以上代码中 runtime
函数是一个带参数的装饰器,它接受一个整数参数 n
,然后返回一个装饰器函数。该装饰器函数内部定义了 wrapper
函数,我们可以在装饰器内部获取到这个参数n(内层函数可以获取到外层函数的变量),上面只是简单的打印了这个参数,我们也可以让这个参数参与到代码运行中。
比如flask
的@app.route
就可以接受参数来决定该方法是get
还是post
。
类装饰器
除了函数装饰器,Python 还支持类装饰器。类装饰器是包含 call
方法的类,它接受一个函数作为参数,并返回一个新的函数。
实例
class DecoratorClass:
def __init__(self, func):
self.func = func
def __call__(self, *args, **kwargs):
# 在调用原始函数之前/之后执行的代码
result = self.func(*args, **kwargs)
# 在调用原始函数之后执行的代码
return result
@DecoratorClass
def my_function():
pass