day 40 打卡-装饰器
装饰器本质上是一个python 函数,它可以让其他函数或者方法在不需要做任何代码修改的前提下增加额外功能。---本质是如果让一个函数具备太多功能,那么它看起来很久就会比较乱,可读性比较差,如果把器中一部分相同甚至可以复用的功能用一个新的函数来调用,然后让2个函数同时实现,就会做到。
1、进一步封装了函数的一些用法,做到dry 原则(don't repeat yourself)
2、使函数更加具有可读性。
所以装饰器本身就是函数,实现先拆分函数,再合并函数的功能。
普通的函数
下面的函数实现的是计算2到9999的所有质数,并且打印找到这些数需要的时间
1、定义一个判断是否为质数
2、定义一个函数,循环2到9999的数,通过判断质数函数来筛选每个数
3、在函数中通过time模块计时
会发现,这个time 模块让整个代码逻辑混乱,因为函数的主体是找质数,time模块是找质数的时间,如果可以让time 模块放在函数外,这样逻辑才清晰。import time
def is_prime(num):if num <2:return Falsefor i in range(2,num):if num % i == 0:return Falsereturn True
def prime_nums():t1=time.time()for i in range(2,10000):if is_prime(i):print(i)t2=time.time()print(t2-t1)
prime_nums()
装饰器函数
import time
# 定义一个装饰器
def display_time(func):def wrapper(*args,**kwargs):start = time.time()func(*args,**kwargs)end = time.time()print(end-start)return wrapper
装饰器的本质是一个高阶函数,它接收一个函数作为参数,并返回一个新函数来替代原函数,这个新函数需要:
1、保留原函数的调用方式(参数和返回值)
2、在原函数执行前添加额外逻辑
因此,我们需要在装饰器内部定义一个新的函数来实现这些功能。
# 继续定义判断质数的函数
def is_prime(num):if num <2:return Falseelif num ==2:return Trueelse:for i in range(2,num):if num % i ==0:return Falsereturn True
# 装饰器的标准写法
@display_time
def prime_nums():for i in range(2,100):if is_prime(i):print(i)
prime_nums()
def prime_nums():...# 函数体
prime_nums=display_time(prime_nums)
装饰器的执行流程为:
1、定义装饰器函数 display_time 它接收一个函数func作为参数,并返回wrapper 函数。
2、定义被装饰函数prime_nums:此时prime_nums是一个普通函数的对象。
3、应用装饰器:python 自动将prime_nums作为参数传递给display_time ,即执行display_time(prime_nums)
4、替换原函数:display_time(prime_nums)返回wrapper 函数,将其赋值给prime_nums ,即prime_nums=wrapper也就是说装饰后,原函数名指向wrapper ,而 非原始函数。
当你调用prime_nums()时,实际上执行的是wrapper(),他会:
1、记录开始时间
2、调用func
3、记录结束时间并打印
这种等价的设计,会让初学者搞不懂为啥突然可以采取这种优雅的 写法,类似的写法有很多,在python 中叫做语法糖:通过规范的语法来让代码更加优美简洁,比如列表推导式也是。
可以吧@ 理解为语法糖操作,实际上并非是@ 装饰器,而是@装饰器+下一行的代码,二者一个整体。进一步扩展装饰器实现复用
可以看到,上述这个写法的时候,prime_nums() 没有传入参数,如果函数有参数,那么必须给外部函数传入参数,也就是需要给外部的装饰器函数传入参数。
那么装饰器函数是需要复用的,不同的内部函数传入的参数不同,那久需要装饰器可以传入可变参数来维持这个特性。这就是说到了我们昨天的可变参数。
装饰器函数返回的是wrapper 函数,所以,在调用装饰器函数的时候,返回的还是wrapper 函数,而不是被修饰的函数。他是被修饰的函数的外层函数,参数要大于等于被修饰函数的参数。
import time
def display_time(func):"""支持任意参数的时间统计装饰器"""def wrapper(*args,**kwargs):t1=time.time()result= func(*args,**kwargs)t2=time.time()print(f"{func.__name__} 运行时间: {t2-t1} 秒")return resultreturn wrapper
@display_time
def add(a,b):return a+b
add(3,5)
最后一个tips:注意下内部函数有无返回值
注意到之前被修饰的函数在无参情况下,wrapper里面只有func(),现在是result=func(*args,**kwargs)以及加上了return result今日作业def logger(func):def wrapper(*args,**kwargs):print(f"调用函数 {func.__name__},参数为 {args} {kwargs}")result=func(*args,**kwargs)print(f"函数 {func.__name__} 返回值为 {result}")return resultreturn wrapper
@logger
def multiply (a,b):return a*b
multiply(2,3)
multiply(a=2,b=3)
@浙大疏锦行