前言
😵😵学到后面越来越难了😫😭,什么装饰器,类,模块,对象,线程看到头大一圈,对于没有计算机基础的我生啃确实有点难,好在大多数问题看多几篇CSDN大神讲解都能看懂,就剩下实战熟练了。
一、什么是装饰器?
👉👉口水理解:简单从字面上去理解,就是一个"锦上添花"的东东,这种锦上添花是不会影响你原来的功能和调用方法的,不同于真正的"锦上添花",这种锦上添花非常非常有用,就想你的笔记本电脑上的键盘膜,有了它也不影响笔记本的功能,但是又多了一个保护键盘的功能,跟手机壳是一个道理的。
👉👉装饰器的三大特性:
1️⃣ 不改变原函数的代码
2️⃣ 不改变原函数的调用方式
3️⃣ 装饰器返回的是函数
二、简易装饰器
✋✋“存在就是合理”,所以我们需要去理解它存在的意义。
首先要理解一个概念或者一种做法,就是函数也是可以作为另外一个函数的参数进行传参运行的。
看下面的栗子🌰:
import time
def printName():
print('I am 峰勇力.')
time.sleep(1) # 这里用睡眠时间模拟该函数需要运行的时间,实际上我们不知道这个时间的
def control(func):
print('control函数在运行。')
func()
print('printName被包围了。')
time.sleep(1)
if __name__ == '__main__':
control(printName) # 将函数printName传参给函数control
# 输出
control函数在运行。
I am 峰勇力.
printName被包围了。
>>>
理解了这个操作,那下面才能更好理解。
现在来一个简单的模拟装饰器写法:
看例子🌰:
# 需求:计算函数printName的运算时间。
# 方法1:不用装饰器的思维
import time
def printName():
print('I am 峰勇力.')
time.sleep(2) # 这里用睡眠时间模拟该函数需要运行的时间,实际上我们不知道这个时间的
if __name__ == '__main__':
t1 = time.time()
printName()
print('运行时间:{}'.format(time.time() - t1))
# 输出
I am 峰勇力.
运行时间:2.027989625930786
>>>
上面的方法确实可以解决计算函数的运行时间问题,但是你再仔细想想,这只是一个函数,实际情况会比这个复杂很多,让你计算1000个函数的运行时间并比较优化,可以想象方法1代码会冗余而且不够精简,所以衍生出装饰器这个想法的解决办法(我看到的那刻真的觉得太绝了👍👍👍)。
看例子🌰:
# 需求:计算函数printName的运算时间。
# 方法2:简陋的装饰器的思维,不加语法糖
import time
def printName():
print('I am 峰勇力.')
time.sleep(2) # 这里用睡眠时间模拟该函数需要运行的时间,实际上我们不知道这个时间的
def timer(func):
def wrapper():
t1 = time.time()
func()
print('运行时间:{}'.format(time.time() - t1))
return wrapper
if __name__ == '__main__':
printName = timer(printName) # <1>
printName() # <2>
# 输出
I am 峰勇力.
运行时间:2.032205581665039
>>>
这里需要解释一下的是在 <1> 处的运行思路和顺序:
顺序:printName函数 -> wrapper函数 -> printName 这个存储空间;
逻辑:**首先 timer 函数将 printName函数 作为参数传入,然后我们只看 返回(return) ,返回的是wrapper函数,然后将 wrapper函数 命名为 printName 这个空间,实际上现在 <2> 处的printName()调用的就是 wrapper 函数,实际上运行的就是wrapper函数,但是呢,你又把原始的printName函数传给timer当参数了,所以wrapper函数还是会调用函数printName(),有点像是自己调用自己的感觉。😛😛
三、装饰器运用场景(为啥要用)
从上面的演示作用来看,装饰器这种思维是很有用处的,最大的好处就是你原来的代码完全可以不用做修改,就可以知道某一模块的运行情况和获取日志,装饰器的主要功能也是如此,比如插入日志、性能测试、事务处理、缓存、权限校验等场景也是用装饰器来实现的(网上抄的)。😉😉
四、装饰器语法
(一)没有参数的装饰器
上面是没有用装饰器语法糖的写法(我也不知道为啥叫语法糖)😅,大概网上的说法python的语法糖是 **@**这个符号,请看下面用语法糖的栗子🌰:
# 方法3:使用语法糖@
import time
def timer(func):
def wraper():
t1 = time.time()
func()
print('运行时间:{}'.format(time.time() - t1))
return wraper
@timer # <3>
def printName():
print('I am 峰勇力.')
time.sleep(2) # 这里用睡眠时间模拟该函数需要运行的时间,实际上我们不知道这个时间的
printName()
# 输出
I am 峰勇力.
运行时间:2.0204033851623535
可以看到输出的结果跟之前的 方法2的效果是一样的,唯一的区别就是<3>处的 @timer和<1>处的printName = timer(printName),所以从形式上看,其他都一样就这两个不一样,但是结果又是一样的,说明 @timer 和**printName = timer(printName)**是等价的,这样你就明白装饰器的最基础的用法和原理了。
(二)带参数的装饰器
这个可能有点点绕😅😅,直接看栗子吧🌰:
# 方法4:装饰器带参数
import time
def timer_out(process):
print(process)
def timer(func): # <2>
print('正在调用装饰器timer')
def wraper(*args,**kwargs):
t1 = time.time()
func(*args,**kwargs)
print('运行时间:{}'.format(time.time() - t1))
return wraper
# 返回的才是装饰器,记住这个写法。
return timer
@timer_out('调用外围函数timer_out') # <1>
def printName(name):
print('I am {}'.format(name))
time.sleep(2) # 这里用睡眠时间模拟该函数需要运行的时间,实际上我们不知道这个时间的
printName('峰勇力')
# 输出
调用外围函数timer_out
正在调用装饰器timer
I am 峰勇力
运行时间:2.000643730163574
理论上来说,装饰器传入的参数必须是要函数,然后返回是函数,但是也不知道python哪个大神这个想法,竟然用函数带参数然后返回装饰器再用装饰器的用法,妥妥的俄罗斯套娃😅😅
看 <1> 处,函数 timer_out 带了一个参数,这样看上去实际上不能算真正意义上的装饰器,但是timer_out函数返回的却是一个装饰器,后面就是完全按照装饰器的用法运行了,所以它的执行顺序就比较明显了:timer_out --> timer(func)(把函数printName传入) --> wraper(*args,**kwargs) ,因为设置了可以传入任意参数,所以wraper把参数 ‘峰勇力’ 传给printName函数,这样就是实现不同参数的分别传入,也是实现了装饰器的带参数使用。
五、有什么说的不正确的希望跟我说一下😅😅,小学鸡原谅一下
OVER~