一、 什么是装饰器?
把一个函数当作参数传递给另外一个函数,返回一个替代版的函数,本质上就是一个返回函数的函数。在不改变原函数的基础上,给函数增加功能。
二、装饰器的入门
定义一个打印hello!的函数,如何在打印结果之前加*******?
1.使用语法糖:
def outer(f):#装饰器,写好之后可被多个函数使用
def inner():
print('*****')
f()
return inner
@outer #语法糖
def fun():
print('hello!')
fun()
@outer#语法糖,第二个函数也可以使用
def g():
print('星期六')
g()
2.不使用语法糖
def outer(f): #装饰器
def inner():
print('*****')
f()
return inner
def fun():#原本的函数
print('hello!')
a=outer(fun) #定义一个新的函数outer,fun是参数
a() #函数调用
三、bi站学的装饰器例子
**eg1:**打印10000以内的质数并计时
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():#打印10000以内的质数并计时
t1=time.time()
for i in range(2,10000):
if is_prime(i):
print(i)
t2=time.time()
print(t2-t1)
prime_nums()
结果:
如果想要计算每个函数的运行时长,需要在每一个函数里面都加time 计时,一个两个函数还好,但工作后如果几千个函数,这就很麻烦了。所以eg1不建议使用。
**eg2:**引进无参装饰器
import time
def dis_play(fun): #装饰器
def wrapper():
t1=time.time()
fun()
t2=time.time()
print('运行时间为:%s' % (t2 - t1))
return 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
@dis_play #语法糖
def prime_nums():
for i in range(2,10000):
if is_prime(i):
print(i)
prime_nums()
结果:
**eg3:**引进有参装饰器,还是上个例子,打印处10000以内质数的个数,就是说有返回值参数了。
import time
def dis_play(fun): #装饰器
def wrapper():
t1=time.time()
res=fun()
t2=time.time()
print('运行时间为:%s' % (t2 - t1))
return res #函数有返回值时,需将返回值return一下
return 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
@dis_play #语法糖
def prime_nums_count():
count=0
for i in range(2,10000):
if is_prime(i):
count+=1
return count
a=prime_nums_count()
print(a)
结果:
升级版:任意范围内质数的个数
import time
def dis_play(fun): #装饰器
def wrapper(*args): #传入参数
t1=time.time()
res=fun(*args)
t2=time.time()
print('运行时间为:%s' % (t2 - t1))
return res
return 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
@dis_play #语法糖
def prime_nums_count(maxnum):
count=0
for i in range(2,maxnum):
if is_prime(i):
count+=1
return count
a=prime_nums_count(10000)
print(a)
四、如何保留被装饰函数的函数名字和帮助文档信息?
在装饰器函数的下面(闭合函数的上面加:@functools.wraps(fun))
import functools
import random
import string
import time
li = [random.choice(string.ascii_letters) for i in range(100)]
print(time.time()) #打印处自1970年到现在一共多少秒
def timeit(fun):
# @functools.wraps(fun)
def wrapper(*args,**kwargs): #接收可变参数 和 关键字参数
"""这是一个装饰器timeit"""
# 在函数执行之前
start_time = time.time()
# 执行函数
res = fun(*args,**kwargs)
# 在函数执行之后
end_time = time.time()
print('运行时间为:%.6f' %(end_time - start_time))
return res
return wrapper
@timeit
def con_add():
"""这是一个con_add"""
s = ''
for i in li:
s +=i
print(s)
con_add()
print(con_add.__name__)
print(con_add.__doc__)
如果不加@functools.wraps(fun),则显示的是装饰器的信息
五、多个装饰器
多个装饰器装饰函数 从上到下去执行的
def decorator_a(fun):
def inner_a(*args,**kwargs): #接收可变参数和关键字参数
print('Get in inner_a')
fun(*args,**kwargs)
return inner_a
def decorator_b(fun):
def inner_b(*args,**kwargs):
print('Get in inner_b')
fun(*args,**kwargs)
return inner_b
# 多个装饰器装饰函数 从上到下去执行的
@decorator_a
@decorator_b
def f(x):
print('Gat in f')
f(1)
六、装饰器的应用
多个装饰器的应用场景
会采用多个装饰器先验证是否登陆成功 再验证权限是否足够
import inspect
login_session = ['root','admin','redhat']
import functools
def is_login(fun):
@functools.wraps(fun)
def warapper(*args,**kwargs):#('root',)如果有多个可变参数,args会存成元组
print(args)
if args[0] in login_session:
temp = fun(*args,**kwargs)
return temp
else:
print()
print('Error:%s 没有登陆成功' %(args[0]))
return warapper
def is_admin(fun):
@functools.wraps(fun)
#inspect_res 会返回一个字典
# key:形参 value:对应的实参
def wrapper(*args,**kwargs):
inspect_res = inspect.getcallargs(fun,*args,**kwargs)
print('inspect的返回值是:%s' %(inspect_res))
if inspect_res.get('name') == 'root':
temp = fun(*args,**kwargs)
return temp
else:
print('not root user,no permisson add user')
return wrapper
@is_login
@is_admin
def adduser(name):
print('add_user')
adduser('refhat')
结果:
牛刀小试:
练习1:
创建装饰器, 要求如下:
- 创建add_log装饰器, 被装饰的函数打印日志信息;
- 日志格式为: [字符串时间] 函数名: xxx, 运行时间:xxx,
运行返回值结果:xxx·
import functools
import random
import time
def outer(f):
@functools.wraps(f) #显示被修饰函数的函数名,注释文档信息
def innner():
"""
这是一个装饰器函数
"""
t1=time.time()
a=f()
s_time=time.ctime()
t2=time.time()
print('字符串时间:'+s_time)
print('运行时间:%.3fs' %(t2-t1))
print('函数名:%s' %(f.__name__))
print('函数返回值结果:%s' %(a))
return innner
@outer #语法糖
def fun():
st=input('请输入自符:')
return st
fun()
练习2:
基础版(无参数的装饰器)
编写装饰器required_ints, 条件如下:
1). 确保函数接收到的每一个参数都是整数;
2). 如果参数不是整形数, 打印 TypeError:参数必须为整形
import functools
def required_ints(func):
@functools.wraps(func)
def wrapper(*args, **kwargs): # args=(1,2,..)
for i in args:
if not isinstance(i, str):
print('函数所有的参数并非都是int型')
break
else:
res = func(*args, **kwargs)
return res
return wrapper
@required_ints
def add(a, b):
print(a + b)
add(1,2.0)
练习3:
2. 升级版(有参数的装饰器)
编写装饰器required_types, 条件如下:
1). 当装饰器为@required_types(int,float)确保函数接收到的每一个参数都是
int或者float类型;
2). 当装饰器为@required_types(list)确保函数接收到的每一个参数都是list类型;
3). 当装饰器为@required_types(str,int)确保函数接收到的每一个参数都是str或者int类型;
4). 如果参数不满足条件, 打印 TypeError:参数必须为xxxx类型
import functools
def required_type(*kind):
def out(fun):
@functools.wraps(fun)
def wrapper(*args, **kwargs): # args=(1,2,..)
for i in args:
if not isinstance(i,kind):
print('函数所有的参数并非都是题目所要求类型')
break
else:
res = fun(*args, **kwargs)
return res
return wrapper
return out
@required_type(int,str)
def add(a, b):
return a,b
add(1,2.0)