装饰器详解
一、nonlocal关键词
1、作用:将local和enclosing(e中的名字需要提前定义)中的名字统一
2、应用场景:如果想在被嵌套的函数中修改外部函数变量(名字)的值
3、案例
def outer(): num = 0 print(num) # 结果为 0 def inner(): # 如果想在被嵌套的函数中修改外部函数变量(名字)的值 nonlocal num # 将L与E(E中的名字需要提前定义)的名字统一 num = 100 print(num) # 结果为 100 inner() print(num) # 结果为 100 outer()
num =100 def fn(): global num num = 10 def fn2(): global num num = 20 fn2() fn() print(num)
二、开放封闭原则
1、定义:不能修改源代码,不能更改调用方式,但可以对外提供增加新功能的特性
2、开放:修改了源代码,没有更改调用方式,对外调用方式还是原来的,但功能有所增加,不能更改被装饰对象(函数)的调用方式,且达到了增加功能的效果
def fn(): print('fn run1') print('fn run') print('fn run3') fn()
3、封闭:没有修改源代码,但更改了调用方式 。
不能修改被被装饰对象(函数)的源代码
def fn(): print('fn run') fn() def wrap(fn): print('fn run1') fn() print('fn run3') fn() wrap(fn)
三、装饰器
1、装饰器:闭包的一个应用场景。为一个函数添加新功能的工具
def vase(): print('插花') vase() # 结果为 插花 # 增加一个绘画观赏功能:不满足封闭开放原则,修改了源代码 def vase(): print('插花') print('绘画观赏') vase() # 结果为 # 插花 # 绘画观赏 # 增加一个绘画观赏功能,不满足开放封闭原则,更改了调用方式 def vase(): print('插花') vase() def wrap(vase): vase() print('绘画观赏') wrap(vase)
def vase(): print('插花') def wrap(vase): def fn(): print('绘画观赏') return fn vase = wrap(vase) vase()
def vase(): print('插花') # 下方的函数嵌套结构就是装饰器***** def wrap(x): #x=vase def fn(): x() # 原有的vase print('绘画:进行观赏') return fn # 拓展功能后的vase vase = wrap(vase) # 将拓展功能后的功能函数重新赋值给vase vase() # 功能拓展了,且调用方式不变
2、装饰器简化语法
def outer(fn): def inner(): fn() print('绘画观赏') return inner def wrap(fn): def inner(): fn() print('摆放功能') return inner # 语法糖|笑笑语法 @wrap # 总结:一个函数可以被任意一个装饰器装饰,也可以被任意几个装饰器装饰 @outer # 装饰的顺序会影响新增功能的执行顺序 def vase(): print('插花') vase() # 结果为 # 插花 # 绘画观赏 # 摆放功能
四、有参有反的函数被装饰
案例 # 增加一个账号处理功能:3位及以上英文字符或汉字 def check_usr(fn): def inner(usr, pwd): if not (len(usr) >= 3 and usr.isalpha()): print('账号验证失败') return False result = fn(usr, pwd) # login return result return inner # 增加一个密码处理功能:6位及以上英文和数字 def check_pwd(fn): def inner(usr, pwd): if not (len(pwd) >= 6 and pwd.isalnum()): print('密码验证失败') return False return fn(usr, pwd) return inner # 登录功能 @check_usr # login = check_usr(login) = inner @check_pwd def login(usr, pwd): if usr == 'abc' and pwd =='123qwe': print('登录成功') return True print('登录失败') return False res = login('abc', '123qwe') # inner print(res)
五、登录认证案例
is_login = False # 登录状态 def login(): usr = input('usr: ') if not (len(usr) >= 3 and usr.isalpha()): print('账号验证失败') return False pwd = input('pwd: ') if usr == 'abc' and pwd =='123qwe': print('登录成功') is_login = True else: print('登录失败') is_login = False # 完成一个登录状态校验的装饰器 def check_login(fn): def inner(*args, **kwargs): # 查看个人主页或销售功能前:如果没有登录先登录,反之可以进入其功能 if is_login != True: print('你未登录') login() # 查看个人主页或销售 result = fn(*args, **kwargs) return result return inner # 查看个人主页功能 @check_login def home(): print('个人主页') # 销售功能 @check_login def sell(): print('可以销售') home()
五、案例
1、写出完整的装饰器(不用考虑带参装饰器,就是普通装饰器)语法
def wrapper(func): def inner(*args, **kwargs): pass result = func(*args, **kwargs) pass return result return inner
2、有一个计算两个数和的方法,为其添加一个确保两个参数都是int或float类型的装饰器,保证运算不会抛异常
def check_num(func): def inner(n1, n2): b1 = isinstance(n1, int) or isinstance(n1, float) b2 = isinstance(n2, int) or isinstance(n2, float) if not (b1 and b2): print('不能求和') return # 结束掉,不让其进入计算功能 return func(n1, n2) return inner @check_num def add(n1, n2): return n1 + n2 print(add(1, '2'))
3、有一个一次性录入人名并返回人名的方法(人名只考虑存英文),为其添加一个装饰器,使得处理后人名首字母一定大写
def upper_name(func): def inner(): result = func() return result.title() # Owen return inner @upper_name def get_name(): name = input('name: ') # owen return name print(get_name())