- 装饰器的思想:进一步复用
- 函数的装饰器写法
- 注意内部函数的返回值
装饰器
为什么需要装饰器?
- 可读性
- DRY原则:Don't Repeat Yourself
在编写函数代码的时候,发现有的时候,一个函数内部包含了不只一个功能,这与‘单一职责’相悖。此外,如果采取了分离功能后,后面要想实现同样的功能,重复编写依旧浪费时间。而装饰器正是解决上述问题的存在,引用在评论区看到的:解耦一些通用处理或者不必要功能的,尽可能让一个函数只负责一个任务,避免后续维护时散弹式修改代码。
#质数查找
import time
def is_prime(num):
if num < 2:
return False
elif num == 2:
return True
else:
for i in range(2, num):
if num % i == 0:
return False
return True
#函数主体是找质数,但是里面混杂着时间的计算
def prime_nums():
t1 = time.time()
for i in range(2, 10000):
if is_prime(i):
print(i)
t2 = time.time()
print(f"执行时间:{t2 - t1}秒")
prime_nums()
换句话说,装饰器本身就是函数中调用其他函数,实现先拆分函数在合并函数的功能。
基本结构
对于一个装饰器,基本机构包括以下几个部分:
def 装饰器名字(原函数):
def 新函数(*args, **kwargs): #新函数一般命名为wrapper
# 添加的新功能
结果 = 原函数(*args, **kwargs) # 执行原函数
# 还可以添加更多新功能
return 结果
return 新函数
其中,原函数如果有参数传入,那么对应的wrapper函数也应有对应的参数(可变数量的参数);如果原函数有return 结果,那么在wrapper函数中也需要return结果。
针对上面的质数查找的例子,将时间计算单独提取出来,装饰器编写如下:
import time
def display_time(func):
# 定义一个内部函数,在装饰器中wrapper函数是一个常用的函数名,并非强制,约定俗成的
def wrapper(): #原函数无参数,故不传入
start_time = time.time()
func() #调用原函数,func是指装饰器需要修饰的函数,这里func替代的是prime_nums
end_time = time.time()
print('Total time:{:.4f}s'.format(end_time-start_time))
return wrapper # return wrapper是返回函数对象,如果是return wrapper()则是立即执行wrapper函数
增加参数和返回值,再应用装饰器后的代码:
import time
#装饰器
def display_time(func):
# 定义一个内部函数,在装饰器中wrapper函数是一个常用的函数名,并非强制,约定俗成的
def wrapper(*args): #原函数无参数,故不传入
start_time = time.time()
result = func(*args) #调用原函数,func是指装饰器需要修饰的函数,这里func替代的是prime_nums
end_time = time.time()
print('Total time:{:.4f}s'.format(end_time-start_time))
return result
return wrapper # return wrapper是返回函数对象,如果是return wrapper()则是立即执行wrapper函数
#质数查找
def is_prime(num):
if num < 2:
return False
elif num == 2:
return True
else:
for i in range(2,num):
if num % i == 0:
return False
return True
@display_time #这行和下一行是一个整体
def count_prime_nums(maxium):
count = 0
for i in range(2,maxium):
if is_prime(i):
count += 1
return count
result = count_prime_nums(10000)
print(result) #1229
装饰器的执行流程
执行流程包括:定义装饰器函数,定义被装饰函数,应用装饰器,替换原函数。
结合上面的例子,具体的流程是:调用count_prime_nums()后,到装饰器display_time()中执行wrapper()函数。然后记录开始时间,调用count_prime_nums()执行质数查找功能(内部会跳转到is_prime()函数),记录结束时间,返回结果(原函数有返回值时必选)。
在上面流程中的‘替换原函数’的意思是,display_time返回的 wrapper 函数,替换了原函数count_prime_nums。装饰后,原函数名指向 wrapper ,而非原始函数。
作业:
编写一个装饰器 logger,在函数执行前后打印日志信息(如函数名、参数、返回值),用昨天的作业题作为被修饰函数:
def logger(func):
def wrapper(*args,**kwargs):
print("{}函数开始执行".format(func.__name__))
print("Arguments:{}".format(args))
print('Keyword Arguments:{}'.format(kwargs))
result = func(*args,**kwargs)
print('{}函数执行完毕,返回值:{}'.format(func.__name__,result))
return result
return wrapper
@logger
def describe_shape(shape_name,color='black',**size):
if not size:
return f'A {color} {shape_name} with no specific dimensions.'
based = f'A {color} {shape_name} with dimensions: '
dimensions = []
for key,value in size.items():
dimensions.append(f'{key}={value}')
based += ','.join(dimensions) + '.' #字符串拼接
return based
#调用
result = describe_shape('turtle','red',length=5,width=4)
print(result)

931

被折叠的 条评论
为什么被折叠?



