装饰器:装饰其他函数
原则:1.不能改变被装束函数内的源代码;2.不能改变其调用方式。用户看不出来函数被改变。
实现装饰器知识储备:
1.函数也是变量2.高阶函数3.函数嵌套。
高阶函数:把一个函数名当做实参传给另一个函数(不修改被装饰的函数源代码)或者返回值中包含函数名。
具体实现:
公司源代码:共享的一个黄色视频网站。
1 def home(): 2 print("---首页----") 3 4 def america(): 5 print("----欧美专区----") 6 7 def japan(): 8 print("----日韩专区----") 9 10 def henan(): 11 print("----河南专区----") 12 13 home() 14 america() 15 henan()
老总要求,改成想要看视频先进行用户认证,认证通过后判断时候为VIP,是才让看。
方法一:改函数源代码。不行
1 user_status = False #用户登录了就把这个改成True 2 3 def login(): 4 _username = "alex" #假装这是DB里存的用户信息 5 _password = "abc!23" #假装这是DB里存的用户信息 6 global user_status 7 8 if user_status == False: 9 username = input("user:") 10 password = input("pasword:") 11 12 if username == _username and password == _password: 13 print("welcome login....") 14 user_status = True 15 else: 16 print("wrong username or password!") 17 else: 18 print("用户已登录,验证通过...") 19 20 def home(): 21 print("---首页----") 22 23 def america(): 24 login() #执行前加上验证 25 print("----欧美专区----") 26 27 def japan(): 28 print("----日韩专区----") 29 30 def henan(): 31 login() #执行前加上验证 32 print("----河南专区----") 33 34 35 36 home() 37 america() 38 henan()
方法二:不改源代码,改调用方式
1 user_status = False #用户登录了就把这个改成True 2 3 def login(func): #把要执行的模块从这里传进来 4 _username = "alex" #假装这是DB里存的用户信息 5 _password = "abc!23" #假装这是DB里存的用户信息 6 global user_status 7 8 if user_status == False: 9 username = input("user:") 10 password = input("pasword:") 11 12 if username == _username and password == _password: 13 print("welcome login....") 14 user_status = True 15 else: 16 print("wrong username or password!") 17 18 if user_status == True: 19 func() # 看这里看这里,只要验证通过了,就调用相应功能 20 21 def home(): 22 print("---首页----") 23 24 def america(): 25 #login() #执行前加上验证 26 print("----欧美专区----") 27 28 def japan(): 29 print("----日韩专区----") 30 31 def henan(): 32 #login() #执行前加上验证 33 print("----河南专区----") 34 35 36 37 home() 38 login(america) #需要验证就调用 login,把需要验证的功能 当做一个参数传给login 39 # home() 40 # america() 41 login(henan)
用户每次调用时需要执行login(henan),类似的。其实稍一改就可以了呀。改成:
home() america = login(america) henan = login(henan)
但问题在于,还不等用户调用 ,你的america = login(america)就会先自己把america执行了呀。。。。,你应该等我用户调用 的时候 再执行才对呀。。。
想实现一开始你写的america = login(america)不触发你函数的执行,只需要在这个login里面再定义一层函数,第一次调用america = login(america)只调用到外层login,这个login虽然会执行,但不会触发认证了,因为认证的所有代码被封装在login里层的新定义 的函数里了,login只返回 里层函数的函数名,这样下次再执行america()时, 就会调用里层函数啦。。。
代码三:
1 def login(func): #把要执行的模块从这里传进来 2 3 def inner():#再定义一层函数 4 _username = "alex" #假装这是DB里存的用户信息 5 _password = "abc!23" #假装这是DB里存的用户信息 6 global user_status 7 8 if user_status == False: 9 username = input("user:") 10 password = input("pasword:") 11 12 if username == _username and password == _password: 13 print("welcome login....") 14 user_status = True 15 else: 16 print("wrong username or password!") 17 18 if user_status == True: 19 func() # 看这里看这里,只要验证通过了,就调用相应功能 20 21 return inner #用户调用login时,只会返回inner的内存地址,下次再调用时加上()才会执行inner函数 22 23 24 @login#america = login(america) 25 def america(): 26 #login() #执行前加上验证 27 print("----欧美专区----") 28 29 def japan(): 30 print("----日韩专区----") 31 32 @login 33 def henan(): 34 #login() #执行前加上验证 35 print("----河南专区----")
但是此版本不能传参数。稍作更改即可。
1 user_status = False #用户登录了就把这个改成True 2 3 def login(func): #把要执行的模块从这里传进来 4 5 def inner(*args,**kwargs):#再定义一层函数 6 _username = "alex" #假装这是DB里存的用户信息 7 _password = "abc!23" #假装这是DB里存的用户信息 8 global user_status 9 10 if user_status == False: 11 username = input("user:") 12 password = input("pasword:") 13 14 if username == _username and password == _password: 15 print("welcome login....") 16 user_status = True 17 else: 18 print("wrong username or password!") 19 20 if user_status == True: 21 func(*args,**kwargs) # 看这里看这里,只要验证通过了,就调用相应功能 22 23 return inner #用户调用login时,只会返回inner的内存地址,下次再调用时加上()才会执行inner函数 24 25 26 def home(): 27 print("---首页----") 28 29 @login 30 def america(): 31 #login() #执行前加上验证 32 print("----欧美专区----") 33 34 def japan(): 35 print("----日韩专区----") 36 37 # @login 38 def henan(style): 39 ‘‘‘ 40 :param style: 喜欢看什么类型的,就传进来 41 :return: 42 ‘‘‘ 43 #login() #执行前加上验证 44 print("----河南专区----") 45 46 home() 47 # america = login(america) #你在这里相当于把america这个函数替换了 48 henan = login(henan) 49 50 # #那用户调用时依然写 51 america() 52 53 henan("3p")
大致思路如下:通过账号认证后才能执行的函数,在不更改函数源代码和调用方式的情况下。定义一个认证函数,将原函数作为实参传给定义的认证函数。在认证函数内部在定义一个inner函数,次函数实现扩展功能。认证函数执行完毕后返回inner函数。上述函数为装饰器。需要装饰的函数,在函数体前加@login即可。