以下所有的实操全在redhat7.3真机上pycharm
装饰器
装饰器作用:不改变原函数,给原函数的功能进行增加新功能或修改;
装饰器(decorator):
把一个函数当作参数,返回一个替代版的函数
本质上,decorator就是一个返回函数的高阶函数
@decorator:语法塘
@functools.wraps(函数名):获取原函数的帮助手册,成为原函数的帮助手册,如果不写的话得到是装饰器的帮助手册;
“”"
给一个原函数前添加一个是新功能,但是必须调用这个原函数且不许改变原函数的本质
#1、普通的函数调用实现,但是它没有直接调用原函数,违背了需求
def fun1():
print('wang')
def fun2():
print('yi')
fun1()
a = fun2()
#2、利用装饰器调用,无参数的状态
import time
def decorator(func):
#decorator()就是一个装饰器,所以接受一个函数作为参数,并返回一个函数;这里的形参名是函数名可以任意
def wrapper(): #去找wrapper()函数
print(time.time())
func() #这里调用的是原函数f1()函数
return wrapper
#在wrapper()函数内,首先打印日志,再紧接着调用原始函数。
#1.如果这里没有返回函数,那么这个wrapper()函数也就没有什么作用了;
#2.这里必须返回函数名,然后去调用wrapper函数,如果返回函数则是返回的是返回值,就不能调用原函数,违反了需求;
@decorator #装饰器 这里@decorator相当于decorator(f1()),注意:考虑代码的顺序;
def f1(): #这里的函数名就是实参
print('This is a function')
#f = decorator(f1)#注释掉@decorator,用这种方式也可以实现;但是它不是直接调用的原函数
#f()
f1()
#指定参数
代码:
import time
def decorator(func):#这里的形参名是函数名可以任意
def wrapper(*args): #去找wrapper()函数
print(time.time())
func(*args) #这里调用的是f2()函数
return wrapper #这里必须返回函数名,然后去调用wrapper函数,如果返回函数则是返回的是返回值,就不能调用原函数,违反了需求;
@decorator
def f2(func_name1,func_name2):
print('This is function ' + func_name1)
print('wangyibo is handsome ' + func_name2)
f2('zhao','hai')
运行结果:
#存在字典参数
代码:
import time
def decorator(func):#这里的形参名是函数名可以任意
def wrapper(*args,**kwargs): #去找wrapper()函数
print(time.time())
func(*args,**kwargs) #这里调用的是f3()函数
return wrapper
#wrapper()函数的参数定义是(*args,**kwargs),因此,wrapper()函数可以接受任意参数的调用。
##在wrapper()函数内,首先打印日志,再紧接着调用原始函数。
#
##1.如果这里没有返回函数,那么这个wrapper()函数也就没有什么作用了;
#2.这里必须返回函数名,然后去调用wrapper函数,如果返回函数则是返回的是返回值,就不能调用原函数,违反了需求;
#
@decorator
def f3(func_name1,func_name2,**kwargs):
print('This is function' + func_name1)
print('This is function' + func_name2)
print(kwargs)
f3('test1','test2',a = 1,b = 2,c = 'westos' )
运行结果:
装饰器计时
import functools #函数名和帮助手册; import functools是导入functools模块。
import random
import string
import time
li = [random.choice(string.ascii_letters) for i in range(5)]
#print(li)
def timecal(func):#2-2
"""这是一个装饰器"""
@functools.wraps(func)
#只需记住在定义wrapper()的前面加上@functools.wraps(func)即可。
#如果不写@functools.wraps(func),那么对于__name__等属性,
# 经过装饰器装饰的函数con_add()函数的函数名__name__等属性,就变成了wrapper()函数名
#解决办法就是添加 @functools.wraps(func)
def wrapper(*args,**kwargs): #3-2
"""这是wrapper函数"""
start_time = time.time() #4
res = func(*args,**kwargs)#5 #调用了con_add()函数 #7-2
# print('con_add',res)
end_time =time.time() #8
print('运行时间为:%.8f' %(end_time-start_time)) #9
return res #将用运行结果返回到函数的调用 #10-1
return wrapper #3-1
@timecal #2-1
def con_add():#6
"""这是con_add函数"""
s = '' #声明s为空字符
for i in li:
s += (i + ',') #将字符串拼接起来
#测试: print('s:',s)
return s #返回给res #7-1
@timecal
def join_add():
"""这是join_add函数"""
return ','.join(li)
a = con_add()#1 #10-2
#测试:print('res:',a)
print(a)
#比较for循环与连接时间那个快,连接时间快
con_add()
join_add()
print(con_add.__name__)#输出函数名
print(con_add.__doc__) #输出功能注释
装饰器练习:
需求:
#创建装饰器, 要求如下:
#1. 创建add_log装饰器, 被装饰的函数打印日志信息;
#2. 日志格式为: [字符串时间] 函数名: xxx, 运行时间:xxx,
运行返回值结果:xxx
代码:
import time
import functools
def add_log(func):
@functools.wraps(func)
def wrapper(*args,**kwargs):
start_time = time.time()
res = func(*args,**kwargs)
end_time=time.time()
print('[%s]函数名:%s,运行时间:%.6f,运行返回值结果:%d'
%(time.ctime(),func.__name__,end_time-start_time,res)) #time.ctime():更好辨认
return res
return wrapper
@add_log
def add(x,y):
time.sleep(1)
return x+y
add(1,2)
运行结果:
练习:
判断是否登录,只需要判断用户是否在这个列表里即可
#2.登录用户是否为超户
import functools
import inspect
def is_admin(fun):
@functools.wraps(fun)
def wrapper(*args):
#print('res:',args[0])
#inspect.getcallargs返回一个字典,key值:形参 value值:对应的实参
inspect_res = inspect.getcallargs(fun,*args)
print('inspect的返回值:%s' %inspect_res)
#返回值是字典是由于inspect.getcallargs()自动生成的key-values对,
#add_student('root')函数调用传的实参root就是values值,
# 而key值是函数inspect.getcallargs()自动生成的调用的原函数add_student(name)的形参name)
if inspect_res.get('name') == 'root':
res = fun(*args)
return res
else:
print('Permission denied!')
return wrapper
#1,判断是否登录
login_user = ['root','admin','redhat']
def is_login_user(fun):
@functools.wraps(fun)
def wrapper(*args):
if args[0] in login_user:
print('%s登录成功' %args[0])
res = fun(*args)
# print('res:',res)
return res
else:
print('登录失败!')
return wrapper
@is_login_user
@is_admin
def add_student(name): #name是形参
print('添加学生%s...' %name)
add_student('root')
add_student(‘westos’)
带参数的装饰器
编写装饰器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_types(*kinds):
def requied(fun):
@functools.wraps(fun)
def wrapper(*args,**kwargs):
for i in args:
if not isinstance(i,kinds): #isinstance(i,kind):判断对象i类型与kind类型相同返回True,否则False
#print('TypeError:参数必须为',kinds)
raise TypeError('参数必须为%s,%s' % kinds) #raise:抛出异常
else:
res =fun(*args,**kwargs)
return res
return wrapper
return requied
@required_types(float,int) #多层嵌套相当于:required_types(float,int)(add(a,b))
#传的是类型至少是两个类型中的一个,
def add(a,b):
return a+b
print(add(1.0,1.2))
将上面的代码:@required_types(int,int)
多个装饰器
#多个装饰器 相当于装饰器函数的嵌套
从上往下调用,嵌套在一起,一层一层执行
def decorator1(fun1):
def inner1(*args,**kwargs): #**kwargs就是个摆设,无用,也可以不写,只是一贯的套路
print('w1 ,before')
fun1()
print('w1 after')
return inner1
def decorator2(fun2):
def inner2(*args,**kwargs):
print('This is inner2')
print('w2 ,before')
fun2()
print('w2 after')
return inner2
@decorator2 #f =decorator2(f) ,fun2 =inner1函数 当执行完fun2时,就代表要去执行inner1,fun1()执行的是真正的原函数
@decorator1 #f = decorator1 (f),fun1=f原函数 f()=inner1
def f():
print('This is f')
f()
#推荐一种方式 ,debug模式.
#多个装饰器装饰一个函数相当于装饰器函数的嵌套 (相当于把另一个装饰器函数的inner方法作为参数传递给了另一个装饰器函数的形参func接收.) so不明白就看下面吧?
#1. 程序从上到下执行, 加载两个函数到内存. —> 按常理来说应该是先执行decorator2,但是由于decorator1 靠近被装饰的原函数. so 先执行 decorator1装饰器函数
#2. 执行 decorator1装饰器,fun1 形参接收的是f原函数的内存地址, 而f(变量)被指向为—>inner1函数内存地址
#3. 指向decorator2装饰器 , fun2 形参接收的是 f(变量的内存地址 ,也就是inner1的内存地址), 此时的f(变量)被重新指向为inner2 函数的内存地址.
#加载执行过程:
#f(变量)—>inner1,func1形参—>f原函数 ==========>>>> f(变量)----<inner2 ,func2形参---->inner1(inner1函数内存地址被当做参数传给了func2)
#取值执行过程:
#f(变量)就是inner2 函数执行, ----> 打印w2 before -->执行 fun2, 也就是执行inner1函数,打印w1 before
#----->执行fun1形参的值,fun1指向的 f原函数 ,打印 f原函数内的东西 ----> 执行完原函数后,返回到inner1函数继续执行
#----->打印inner1函数 w1 after .------> 执行完inner1函数,返回到inner2函数中,打印 w2 after
#1.哪个装饰器在被装饰器函数的最上面.那个就是最外层.
#2.一层一层嵌套. 如下图
运行结果: