装饰器简介
装饰器,用于装饰某个函数,或者方法,或者类
装饰器可以用于某个函数或者方法或者类在执行之前或者执行后做某些操作,也可以理解为,为某个函数或者方法或者类添加功能
好处:是不用改变原来的函数,就可以给原来的函数添加功能
提示:装饰器本身就是一个函数,只不过其传入参数为一个函数或方法或类,我们这里就主要指其参数为一个函数
方法:
1,定义一个函数,里面传一个值用于接收被装饰函数的名称(叫做装饰器函数)
2,在装饰器函数里定义一个函数(叫做装饰器功能函数)来写添加功能和执行被装饰函数
3,在装饰器功能函数return返回被装饰函数给装饰器功能函数,并且在装饰器函数return返回装饰器功能函数给装饰器函数
4,在被装饰函数上面写上 @装饰器函数名 就可以调用执行装饰器函数
重点:
只要函数应用上了装饰器,那么函数就会被重新定义,重新定义为装饰器的内层函数,装饰器内层函数的返回结果就等于应用了装饰器的函数结果
较标准装饰器的书写
1 #!usr/bin/python3 2 from functools import wraps # 导入wraps下文会用到 3 4 def decorator(func): # 装饰器也是一个函数,其参数也为一个函数 5 @wraps(func) # 利用 @wraps(func) 装饰wrapper避免装饰器改变原函数的名字 6 def wrapper(*args, **kwargs): # wrapper(*args, **kwargs) 万能参数,不管被装饰函数有多少参数,也不会出错 7 print('我在被装饰函数执行前执行!!') # 在被装饰函数执行前执行的操作 8 func(*args, **kwargs) # 这里同样采用万能参数 9 print('我在被装饰函数执行后执行!!') # 在被装饰函数执行后执行的操作 10 return wrapper # 返回值为函数wrapper,没小括号代表不执行 11 12 13 @decorator # 装饰器使用方法 14 def run(): 15 print('Hello World!') 16 # 用了装饰器之后的函数实际上等价于: run = decorator(run) 17 # 因为装饰器返回的是wrapper函数,所以: run = wrapper 18 19 run() # 所以调用run() 等价于调用 wrapper() 20 21 # 运行结果为: 22 # 我在被装饰函数执行之前执行! 23 # Hello World! 24 # 我在被装饰函数执行之后执行!
内容解析1:为什么要用到万能参数 (*args, **kwargs)
1 from functools import wraps 2 3 def decorator(func): 4 @wraps(func) 5 def wrapper(): # 这里取消了万能参数,选择不用参数 6 print('我在被装饰函数执行前执行!!') 7 func() # 这里取消了万能参数,选择不用参数 8 print('我在被装饰函数执行后执行!!') 9 return wrapper 10 11 12 @decorator 13 def run(): 14 print('Hello World!') 15 16 @decorator 17 def fff(a): 18 print(a) 19 20 run() # run函数将能成功的调用 21 fff(5) # fff函数将不能成功的调用
上面代码,取消了装饰器功能函数和其中被传入函数的万能参数,使其无参。这样,当装饰器装饰有参的函数时将会报错,不能正确执行。因为用了装饰器之后,本质上调用函数时调用的是wrapper函数,wrapper本身定义时是无参的,但调用的时候你却给其传入参数,其将不能正确执行。
同理,我们给定义装饰器的时候给装饰器功能函数wrapper一个参数,其对没有参数的函数或者对有更多参数的函数进行装饰的时候,也不能正确执行。
所以我们这里就必须用到万能参数 (*args, **kwargs),这个万能参数代表可以没有参数、也可以有多个参数、或者一个或多个关键字参数。这里万能参数只是我对它的一个称呼,感觉这样叫还是挺合理的。
内容解析2:from functools import wraps , @wraps(func) 的作用
1 def run(): 2 print('Hello World!') 3 4 print(run.__name__) # 打印run函数的名字,将打印出run
我们单独定义一个函数 run,打印出它的名字为 run 这是很正常的
1 from functools import wraps 2 3 def decorator(func): 4 # @wraps(func) 注释掉了这一行 5 def wrapper(*args, **kwargs): 6 print('我在被装饰函数执行前执行!!') 7 func(*args, **kwargs) 8 print('我在被装饰函数执行后执行!!') 9 return wrapper 10 11 12 @decorator 13 def run(): 14 print('Hello World!') 15 16 print(run.__name__) # 打印内容为: wrapper
这里我们注释掉了第四行的内容,用装饰器装饰函数 run,打印函数 run 的名字,会惊奇的发现函数 run 的名字竟然不是 run。其实这也不是太难理解,上文中提到调用函数 run 本质上就是调用装饰器功能函数 wrapper。所以在使用一个装饰器的时候,它会把原函数的名字改变掉,这在编程里是一个十分危险的事情,所以必须加以避免。而避免方法就是从functools模块导入wraps并用其去装饰【装饰功能函数】。
1 from functools import wraps 2 3 def decorator(func): 4 @wraps(func) 5 def wrapper(*args, **kwargs): 6 print('我在被装饰函数执行前执行!!') 7 func(*args, **kwargs) 8 print('我在被装饰函数执行后执行!!') 9 return wrapper 10 11 12 @decorator 13 def run(): 14 print('Hello World!') 15 16 print(run.__name__) # 打印内容为: run
去掉注释,用wraps装饰【装饰功能函数】,并且参数为 func,也就是装饰器的参数。此时再打印函数 run 的名字,打印出 run,从而避免了装饰器对原函数名字的影响。