装饰器
装饰器流程剖析
一、装饰器概要
1、需求
基础平台--底层代码的开发实现
不同业务线--底层业务的实现,调用底层代码
底层部门100个函数用来支撑85个业务线
要求:在所有的功能里面加个写日志的功能
1.1、所有业务线的人改
1.2、底层代码这边进行修改(☑️)
方法一、在底层代码s2文件里面直接定义一个函数,专门用来修改这个日志的
s2文件
#!/user/bin/env python # -*- coding: utf-8 -*- #100个业务线 def outer(): print('log') def f1(): outer() print('F1') def f2(): outer() print('F2') def f100(): outer() print('F100')
b1(业务线1)文件
#!/user/bin/env python # -*- coding: utf-8 -*- import s2 s2.f1() s2.f2() #打印结果 ''' log F1 log F2 '''
方法二、使用装饰器
s2文件
#!/user/bin/env python # -*- coding: utf-8 -*- #100个业务线 def outer(func): def inner(): print('log') #在执行函数前打印log ret = func() print('after') #在执行函数后打印after return ret return inner @outer def f1(): print('F1') @outer def f2(): print('F2') @outer def f100(): print('F100')
b1文件
#!/user/bin/env python # -*- coding: utf-8 -*- import s2 s2.f1() s2.f2() #打印结果 ''' log F1 after log F2 after '''
方法一,修改了每一个函数里面内部的功能(加了outer()),在软件开发的时候,应该遵循开放封闭原则
方法二、开放封闭原则:不修改函数内部代码,在函数外部额外再添加一个功能
2、概念
开放封闭原则:对于类、函数的内部对于所有人来说是封闭的,改功能的时候,在函数的外部是开放的,所以只能在函数的外部进行修改
在不改变调用函数的基础上,在执行函数前或者执行函数后添加一个功能
3、装饰器内部结构原理解析
f2中的xxx参数为f1函数的整体,如下代码所示
#!/user/bin/env python # -*- coding: utf-8 -*- def f1(): print(123) def f1(): print(456) f1() #执行的是456
def f1(): print('123') def f2(xxx): xxx() f2(f1) #f1是函数f1的整体 与f1()不同 #对于函数来说,函数的整体是可以当作参数来进行传递的
二、装饰器流程剖析
1、函数执行过程剖析
一个函数只是定义的时候,解释器去执行代码的时候是不会执行的
1.1、@符号
@+函数名 并且放在某个函数的上面
1.2、功能
自动执行outer函数并且将其下面的函数名f1当作参数传递;
将outer函数的返回值,重新赋值给f1
详见下面代码👇:
def outer(func): return '111' @outer def f1(): print('F1') print(f1)
#打印结果:111 ''' 等于执行了 f1 = '111', 不执行print('F1') '''
2、将返回值换成函数
2.1、将上面的return '111'替换成def inner(): print('before'),等同于将f1赋值成了def inner(): print('before') 这个函数,而不是print('F1')
所以最终打印的结果就是before
#!/user/bin/env python # -*- coding: utf-8 -*- #100个业务线 def outer(func): def inner(): print('before') return inner @outer def f1(): print('F1') print(f1) #打印结果:before
2.2、在执行print('before')的同时,也进行执行print('F1')
如下代码中的func参数指的是老的f1函数(def f1(): print('F1'))
func():执行def f1(): print('F1')
#!/user/bin/env python # -*- coding: utf-8 -*- #100个业务线 def outer(func): def inner(): print('before') #输出before func() #输出F1 return inner @outer def f1(): print('F1') #打印结果: ''' before F1 '''
2.3、在执行函数之后print一个after
#!/user/bin/env python # -*- coding: utf-8 -*- #100个业务线 def outer(func): def inner(): print('before') #输出before func() #输出F1 print('after') return inner @outer def f1(): print('F1') #打印结果: ''' before F1 after '''
3、总结
3.1、定义函数未调用,函数内部不执行
3.2、函数名:代指整个函数(不加括号)
3.3、执行顺序:4-11、12、13-5-9-6-7-13-8
1 #!/user/bin/env python 2 # -*- coding: utf-8 -*- 3 #100个业务线 4 def outer(func): 5 def inner(): 6 print('before') #输出before 7 func() #输出F1 8 print('after') 9 return inner #函数整体,如果是inner()的时候,因为inner函数没有返回,所以返回的是None,系统会处于瘫痪状态 10 11 @outer 12 def f1(): 13 print('F1') 14 15 16 #打印结果: 17 ''' 18 before 19 F1 20 after 21 '''
三、装饰器流程剖析之返回值
1、应用场景
1.1、在不改变原函数内容的前提下,再之前或者之后统一的做一些操作
2、返回值
2.1、如下代码中,f1函数(原函数)没有返回值,返回值是一个None
s2文件
#!/user/bin/env python # -*- coding: utf-8 -*- #100个业务线 def outer(func): def inner(): print('before') #输出before func() #输出F1 print('after') return inner() @outer def f1(): print('F1')
b1文件
#!/user/bin/env python # -*- coding: utf-8 -*- import s2 ret = s2.f1() print('返回值',ret) #打印结果 ''' before F1 after 返回值 None '''
2.2、原函数(f1())有返回值
return r需要在print('after')的后面,因为不然函数执行过程中遇到了return就不会再执行下面的函数了
无法实现,在执行原函数之后执行后面的操作
s1文件
#!/user/bin/env python # -*- coding: utf-8 -*- #100个业务线 def outer(func): def inner(): print('before') #输出before r = func() #输出F1,并且将f1中返回值返回给了r print('after') return r #将返回的“有返回值”这个原函数的返回值返回 return inner @outer def f1(): print('F1') return "有返回值"
b1文件
#!/user/bin/env python # -*- coding: utf-8 -*- import s2 ret = s2.f1() print('返回值',ret) #打印结果 ''' before F1 after 返回值 有返回值 '''
四、装饰器流程剖析之参数
1、应用装饰器之前,原函数有参数
s2文件
def f1(arg): print(arg) return "有返回值"
b1文件
#!/user/bin/env python # -*- coding: utf-8 -*- import s2 ret = s2.f1('原函数有参数的参数') print('返回值',ret) #打印结果 ''' 原函数有参数的参数 返回值 有返回值 '''
2、应用了装饰器之后的,原函数只有一个原函数f1(arg)
2.1、错误实例
解析:因为应用了装饰器之后,f1中的函数执行的是inner()函数,但是inner()没有参数,所以报错
s2文件
#!/user/bin/env python # -*- coding: utf-8 -*- #100个业务线 def outer(func): def inner(): print('before') #输出before r = func() #输出F1 print('after') return r return inner @outer def f1(arg): print(arg) return "有返回值"
b2文件
#!/user/bin/env python # -*- coding: utf-8 -*- import s2 ret = s2.f1('原函数有参数的参数') print('返回值',ret) #打印结果 ''' Traceback (most recent call last): File "/Users/liyueting/sday13/b1.py", line 6, in <module> ret = s2.f1('原函数有参数的参数') TypeError: inner() takes 0 positional arguments but 1 was given '''
2.2、正确实例
解析:在inner()与func()函数都需要加上参数,并且两处加的参数需要是相同的,不然会报错
s2文件
#!/user/bin/env python # -*- coding: utf-8 -*- #100个业务线 def outer(func): def inner(a): print('before') #输出before r = func(a) #输出F1 print('after') return r return inner @outer def f1(arg): print(arg) return "有返回值"
b1文件
#!/user/bin/env python # -*- coding: utf-8 -*- import s2 ret = s2.f1('原函数有参数的参数') print('返回值',ret) #打印结果 ''' before 原函数有参数的参数 after 返回值 有返回值 '''
3、f1有一个参数、f2有两个参数
3.1、错误实例
s2文件
#!/user/bin/env python # -*- coding: utf-8 -*- #100个业务线 def outer(func): def inner(a): print('before') #输出before r = func(a) #输出F1 print('after') return r return inner @outer def f1(arg): print(arg) return "有返回值" @outer def f2(arg1,arg2): print("F2")
b1文件
#!/user/bin/env python # -*- coding: utf-8 -*- import s2 ret = s2.f1('原函数有参数的参数') print('返回值',ret) s2.f2(11,22) #打印结果 ''' before 原函数有参数的参数 after 返回值 有返回值 Traceback (most recent call last): File "/Users/liyueting/sday13/b1.py", line 9, in <module> s2.f2(11,22) TypeError: inner() takes 1 positional argument but 2 were given '''
3.2、正确实例
s2文件
#!/user/bin/env python # -*- coding: utf-8 -*- #100个业务线 def outer(func): def inner(*args,**kwargs): #使用万能参数,支持传无数个参数 print('before') r = func(*args,**kwargs) #使用万能参数,支持传无数个参数 print('after') return r return inner @outer def f1(arg): print(arg) return "有返回值" @outer def f2(arg1,arg2): print("F2")
b1文件
#!/user/bin/env python # -*- coding: utf-8 -*- import s2 ret = s2.f1('原函数有参数的参数') print('返回值',ret) s2.f2(11,22) #打印结果 ''' before 原函数有参数的参数 after 返回值 有返回值 before F2 after '''
五、用户管理程序实例
1、需求
利用装饰器做一个登录的功能,具体功能为:登录一个网站的时候,部分功能进行权限判断或者是账号不能查看
2、相关代码
2.1、不采用装饰器
LOGIN_USER = {'is_login':False} def order(): if LOGIN_USER['is_login']: print('欢迎%s登录' %LOGIN_USER['current_user']) else: print('请登录') def changepwd(): if LOGIN_USER['is_login']: print('欢迎%s登录' %LOGIN_USER['current_user']) else: print('请登录') def manager(): if LOGIN_USER['is_login']: print('欢迎%s登录' %LOGIN_USER['current_user']) else: print('请登录') def login(user,pwd): if user == 'zhangsan' and pwd =='123456': LOGIN_USER['is_login'] = True LOGIN_USER['current_user'] = user manager() def main(): while True: inp = input('1,后台登录;2,登录') if inp == '1': manager() elif inp == '2': username = input('请输入用户名') pwd = input("请输入密码") login(username,pwd) main()
2.2、利用装饰器
LOGIN_USER = {'is_login':False} def outer(func): def inner(*kags,**kwargs): if LOGIN_USER['is_login']: r = func() return r else: print('请登录') return inner @outer def order(): print('欢迎%s登录' %LOGIN_USER['current_user']) @outer def changepwd(): print('欢迎%s登录' %LOGIN_USER['current_user']) @outer def manager(): print('欢迎%s登录' %LOGIN_USER['current_user']) def login(user,pwd): if user == 'zhangsan' and pwd =='123456': LOGIN_USER['is_login'] = True LOGIN_USER['current_user'] = user manager() def main(): while True: inp = input('1,后台登录;2,登录') if inp == '1': manager() elif inp == '2': username = input('请输入用户名') pwd = input("请输入密码") login(username,pwd) main()
双层及多层装饰器
一、双层装饰器
1、dict的get功能
USER_INFO = {} USER_INFO.get('is_login',None) #如果字典中没有相对应的value的时候,用get方法的时候需要将value默认为None
2、登录身份区分
普通用户:只要登录就可以查看
超级管理员:登录+相对应的权限
3、利用多个重复的装饰器完成
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
USER_INFO = {} def check_login(func): """ 普通用户的装饰器 :param func: :return: """ def inner(*args,**kwargs): if USER_INFO.get('is_login',None): ret = func(*args,**kwargs) return ret else: print('请登录') return inner def check_admin(func): """ 超级管理员的装饰器 :param func: :return: """ def inner(*args,**kwargs): if USER_INFO.get('is_login',None): if USER_INFO.get('user_type',None)==2: ret = func(*args,**kwargs) return ret else: print('无权限查看') return inner @check_admin def index(): #超级管理员 """ :return: """ print('Index') @check_login def home():#普通管理员 """ :return: """ print('home') def login(): user = input('请输入用户名:') if user == 'admin': USER_INFO['is_login'] = True USER_INFO['user_type'] = 2 else: USER_INFO['is_login'] = True USER_INFO['user_type'] = 1 def main(): while True: inp = input('1、登录;2、查看信息;3、超级管理员管理 \n >>>') if inp == '1': login() elif inp == '2': home() elif inp == '3': index() main() #打印结果 ''' 1、登录;2、查看信息;3、超级管理员管理 >>>1 请输入用户名:zhangsan 1、登录;2、查看信息;3、超级管理员管理 >>>2 home 1、登录;2、查看信息;3、超级管理员管理 >>>3 无权限查看 1、登录;2、查看信息;3、超级管理员管理 >>>1 请输入用户名:admin 1、登录;2、查看信息;3、超级管理员管理 >>>2 home 1、登录;2、查看信息;3、超级管理员管理 >>>3 Index 1、登录;2、查看信息;3、超级管理员管理 >>> '''
4、利用双层装饰器完成
4.1、相关代码
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
USER_INFO = {} def check_login(func): """ 普通用户的装饰器 :param func: :return: """ def inner(*args,**kwargs): if USER_INFO.get('is_login',None): ret = func(*args,**kwargs) return ret else: print('请登录') return inner def check_admin(func): """ 超级管理员的装饰器 :param func: :return: """ def inner(*args,**kwargs): if USER_INFO.get('user_type',None)==2: ret = func(*args,**kwargs) return ret else: print('无权限查看') return inner @check_login @check_admin def index(): #超级管理员 """ :return: """ print('Index') @check_login def home():#普通管理员 """ :return: """ print('home') def login(): user = input('请输入用户名:') if user == 'admin': USER_INFO['is_login'] = True USER_INFO['user_type'] = 2 else: USER_INFO['is_login'] = True USER_INFO['user_type'] = 1 def main(): while True: inp = input('1、登录;2、查看信息;3、超级管理员管理 \n >>>') if inp == '1': login() elif inp == '2': home() elif inp == '3': index() main() #打印结果 ''' 1、登录;2、查看信息;3、超级管理员管理 >>>3 请登录 1、登录;2、查看信息;3、超级管理员管理 >>>1 请输入用户名:admin 1、登录;2、查看信息;3、超级管理员管理 >>>3 Index 1、登录;2、查看信息;3、超级管理员管理 >>> '''
4.2、多个装饰器的执行顺序
10-11-24-25-38-26-12
1 USER_INFO = {} 2 3 def check_login(func): 4 """ 5 普通用户的装饰器 6 :param func: 7 :return: 8 """ 9 def inner(*args,**kwargs): 10 if USER_INFO.get('is_login',None): 11 ret = func(*args,**kwargs) 12 return ret 13 else: 14 print('请登录') 15 return inner 16 17 def check_admin(func): 18 """ 19 超级管理员的装饰器 20 :param func: 21 :return: 22 """ 23 def inner(*args,**kwargs): 24 if USER_INFO.get('user_type',None)==2: 25 ret = func(*args,**kwargs) 26 return ret 27 else: 28 print('无权限查看') 29 return inner 30 31 @check_login 32 @check_admin 33 def index(): #超级管理员 34 """ 35 36 :return: 37 """ 38 print('Index') 39 index()
4.4、相关执行流程
本质:一个装饰器与一个原函数结合之后 = 新的函数
函数名:index
函数体:详见下图
执行顺序:nindex-index(第一个)
执行顺序:nnindex--nindex-index(第二个)
4.5、整体的执行顺序:
先执行check_login里面的inner函数--再执行check_adim里面的inner的函数--再执行index下面的函数
执行:从上往下执行
@check_login @check_admin def index(): #超级管理员 """ :return: """ print('Index')
解释:从下往上一步一步编译的
二、多层装饰器
与双层装饰器一样
三、其他装饰器
详见:http://www.cnblogs.com/wupeiqi/articles/4980620.html