今天我们讲一讲python中的装饰器。可能初次接触装饰器的同学会觉得它很难,其实也就那么一回事儿,今天就让我们会会它!
装饰器
首先它的本质是函数,它的功能是为其他函数添加附加功能。
ps:它有两个原则:
1.不能修改被修饰的函数的代码。
2.不能更改被修饰函数的调用函数。
我所认为的装饰器其实就是:
装饰器 = 高阶函数+函数嵌套+闭包
我们先说什么是高阶函数?
当函数(符合其中任意一个):
- 函数接收的参数是一个函数名。
- 函数的返回值是一个函数名。
就是把高阶函数。
那么我们考虑当只有高阶函数时,我们能否写出一个装饰器?
现在我们只用高阶函数实现:运行foo函数时,添加功能:计算其运行时间并打印。试试看吧!
# 装饰器 = 高阶函数 + 函数嵌套 + 闭包
# 先玩玩高阶函数
import time
def foo():
time.sleep(3)
print('来自foo')
def timer(func):
start_time = time.time()
func()
stop_time = time.time()
print('函数的运行时间是%s' % (stop_time-start_time))
timer(foo) # 改变了函数的调用方式
import time
def foo():
time.sleep(3)
print('来自foo')
def timer(func):
start_time = time.time()
func()
stop_time = time.time()
print('函数的运行时间是%s' % (stop_time-start_time))
return func
foo = timer(foo) # 没改变调用方式也没改变函数源代码但多运行了一次,不合格
foo()
# 总结高阶函数独自完不成装饰器
分析上述两段代码,我们会发现:第一种方式改变了函数的调用方式,第二种则多运行了一句代码。所以我们总结:高阶函数独自完不成装饰器!
所以我们需要函数嵌套,和闭包的概念。
函数嵌套:在函数中再定义函数。
闭包:优先使用自己的作用域。
这两个概念都是比较简单的,在这里不作过多解释。下面粘一段代码,供大家参考。
# 函数的嵌套与闭包
def father(name):
print('我是%s' % name)
def son():
print('我的父亲是%s' % name)
def ganud():
print('我的爷爷是%s' % name)
ganud()
son()
# 闭包先使用自己的作用域。
father('liuruochen')
"""
最后结果是:
我是liuruochen
我的父亲是liuruochen
我的爷爷是liuruochen
"""
到这里大家对高阶函数,函数嵌套,闭包应该已经明了。
而装饰器=高阶函数+函数嵌套+闭包。
我们通过一段代码来分析。代码依旧是指行一个函数,通过装饰器给其附加一个计算运行时间的功能。
# 给test函数添加计算运行时间的附加功能
import time
def timer(func): # 传进去test
def warrp(*args, **kwargs): # args = (name, age)
# print(args)
start_time = time.time()
res = func(*args, **kwargs) # 运行test , *args = name, age
stop_time = time.time()
print('函数的运行时间是%s' % (stop_time-start_time))
return res
return warrp
@ timer # @ timer 就相当于进行了 test = timer(test)
def test(age, name):
time.sleep(3)
print('【%s】年龄大的【%s】函数运行完毕!' % (age, name))
return '这是fan'
# test = timer(test) # 这里拿到的是warrp的地址,当没有@timer时必须多谢这句代码。
res = test(18, 'liu') # 实际上运行的是warrp
print(res)
"""
结果:
【18】年龄大的【liu】函数运行完毕!
函数的运行时间是3.0004591941833496
这是fan
"""
大家可以看到通过@timer装饰器,我们使用test函数就多出了一种附加功能!
注:
给大家分析一下它的过程。
首先,导入time模块,定义test函数,timer函数(我说过装饰器本质就是一个函数)。
我们主要分析这个timer函数,大家可以看到,它的传参是一个函数名,返回值也是一个函数名。那么:timer(test)得到的是warrp函数的地址。我们进行test = timer(test)之后,这时的test其实变成了warrp函数。然后我们再进行test(18,“liu”)实际上就是进行warrp(18,liu)
(这里注意warrp参数的问题 args **kwargs 这里不做解释,如果需要文末会有一些博客解释它们。)。之后在函数warr()中执行了test()函数。
https://www.jianshu.com/p/821492b3d3c6 :解释arg **kwarg问题。
来源:CSDN
作者:唯尘
链接:https://blog.csdn.net/qq_42839596/article/details/104617160