- 定义(如何理解装饰器):装饰器本生是闭包函数的一种应用,是指在不改变原函数的情况下为原函数添加新的功能的一个函数。它把被装饰的函数作为外层函数的参数传入装饰器,通过闭包操作后返回一个替代版函数。
- 遵循的原则: 开放封闭原则------在不改变调用方式和源代码的情况下,增加新功能。
- 不能改变被修饰对象(函数(后面还会对类进行装饰))的源代码(封闭)
- 不能改变被修饰对象(函数(以后还会对类进行装饰))的调用方式,且能达到增加功能的效果。(开放)
- 优点:
- 丰富了原函数的功能
- 提高了程序的可拓展性
- 装饰器的简单实现:
# 无参装饰器公式: def fn(): print('原功能') def outer(fn): def inner(): print('新功能1') fn() print('新功能2') return inner fn = outer(fn1) print(fn())
""" 案例分析: 定义一个打印插花的函数,为其前面添加绘画功能,后面添加观赏功能。 如何一步一步完成完整的装饰器函数。 """ def vase(): print('插花') def wrap(): tag = vase # 此时的vase 是在wrap()还没有运行结束的时候,wrap没有返回值,所以tag被赋值的是vase原先的值。 def outer(): print('绘画') tag() # 原先的vase print('观赏') return outer # wrap()函数返回的是outer函数的地址空间 vase = wrap() # vase = outer vase()
上述代码,存在一个缺陷,就是没有写活,没办法将装饰器用在其他函数上,对于无参装饰器的正确的姿势应该如下:
def vase(): print('插花') def wrap(fn): def outer(): print('绘画') fn() # 原先的vase print('观赏') return outer vase = wrap(vase) # vase = outer() vase()
那么如何让代码更简洁? python 提供一种语法糖,可以让代码看起来更简洁
def wrap(fn): def outer(): print('绘画') fn() # 原先的vase print('观赏') return outer @wrap # 此语法糖形式,实际上就是完成了 vase = wrap(vase)操作 = outer def vase(): print('插花') print(vase())
下面来看看如何进行多层装饰:(多层装饰注意一个顺序问题,执行时是从上向下执行,返回操作的时候是一层一层跳出)
def wrap1(fn): def outer(): print('绘画') fn() # 原先的vase print('观赏') return outer def wrap2(fn): def outer(): print('购买') fn() # 原先的vase return outer @wrap2 @wrap1 def vase(): print('插花') vase() # 购买 # 绘画 # 插花 # 观赏 @wrap1 @wrap2 def vase(): print('插花') vase() # 绘画 # 购买 # 插花 # 观赏
5.有参有返的装饰器
def wrap(func): def inner(*args, **kwargs): # 此处 *args (接收所有位置参数) 接收(a,b,c)以元组形式存储,kwargs (接收所有关键字参数) 则是接收{x:4,y:5,z:6}存成字典形式 print('新功能1') res = func(*args, **kwargs) print('新功能2') return res return inner @wrap def fn(a, b, c, *, x, y, z): print(a, b, c, x, y, z) re = fn(1, 2, 3, x=4, y=5, z=6) print(re) # 新功能1 # 1 2 3 4 5 6 # 新功能2 # None 返回值是None 因为函数fn本身无return
案例:在原登录功能下增加账号验证和密码验证功能
# 增加一个账号安全处理功能:3位英文字母或汉字 def check_pwd(fn): def inner(usr, pwd): if not (len(pwd) >= 6 and usr.isalpha()): print('账号认证失败') return False return fn(usr, pwd) return inner # 增加一个密码处理功能 : 6位以上英文和数字组合 def check_usr(fn): # login = check_usr(login) = inner def inner(usr, pwd): if not (len(usr) >= 3 and usr.isalnum()): print('密码认证失败') return False return fn(usr, pwd) return inner @check_usr # login = inner # 语法糖 谁现在下面谁先装功能,套在最外面(上面的)的是最先执行的 @check_pwd # 登录功能 def login(usr, pwd): if usr == 'qwe' and pwd == '123123': print('登录成功') return True print('登录失败') return False res1 = login('asdd', '123') res2 = login('as', '123123') res3 = login('qwe', '123') res4 = login('qwe', '123123') print(res1) print(res2) print(res3) print(res4) # 账号认证失败 # 密码认证失败 # 账号认证失败 # 登录成功 # False # False # False # True
带参装饰器(def wrap(参数们))
# 通过带参装饰器,实现增加颜色选择功能 def color_choice(color_inp): def wrap(fn): if color_inp == 'red': info = 'red:new action' else: info = 'yellow:new action' def inner(*args, **kwargs): res = fn(*args, **kwargs) print(info) return res return inner # wrap(fn) 返回的是 inner函数对象(地址) return wrap # color_choice 返回的是 wrap函数对象 color_choice(color_inp)() => wrap() => res color_inp = input('color:') @color_choice(color_inp) def func(): print('func run ') func()
案例:查看个人主页前的登录状态验证
is_login = False def login(): usr = input('usr:').lower() if not (len(usr) >= 3 and usr.isalnum()): print('密码认证失败') return False pwd = input('pwd:') if usr == 'qwe' and pwd == '123123': print('登录成功') is_login = True else: print('登录失败') is_login = False # 完成一个登录状态校验的装饰器 def check_login(fn): def inner(*args, **kwargs): if is_login != True: print('你未登录') login() res = fn(*args, **kwargs) return res return inner # 查看个人主页功能 @check_login def homepage(): print('个人主页') @check_login # 销售功能 def sell(): print('销售页面') homepage()