1、闭包
①、python中一切皆对象,函数中嵌套函数肯定也是合理的,这种函数也叫作内嵌函数(内部函数)
②、内嵌函数只能被外部函数调用,被外部函数以外的区域被调用,就会出错!!!
③、若内部函数中引用了外部函数的变量,或者外部函数以外的变量,那么!!这个内部函数就叫做闭包
例子:
④、闭包的作用:定义-->闭包可以将其自己的代码和作用域以及外部函数的作用结合在一起
按照上面的例子通俗的理解闭包就是:
1 本来count仅实现了两个数量相加的功能
2
3 由于闭包函数sum,给这个原有的的功能上又新增了一个功能
4
5 因此可得,增加了闭包函数,在不改变原有功能的情况下修饰了原有函数,。使函数的功能可以变得更丰富---->由此引入装饰器的概念,其实和闭包的作用都是一样的,都是为了装饰原有函数的功能
2、装饰器
①、理解了上述闭包的概念,那么其实装饰器的本质也是一个函数(确切的说是一个闭包函数)
②、常见使用场景:插入日志(这个在测试过程中有用)、性能测试、事务处理、权限校验
③、用法:创建一个装饰器,其实就是创建一个普通def函数
④、有返回值的函数被装饰之后依然有返回值,没有返回值的函数被装饰之后则没有返回值,符合我们想要的结果。
1
#装饰器不正规的书写方法
def decorator(func):----->装饰器的参数是一个方法(方法名), 2 func() 3 print('this is decorator')---->正常情况下,装饰器初始化完成,装饰器应该(return)返回一个可调用的对象,很明显,这边没有 4 5 @decorator 6 def target():---->原功能函数,其功能是打印一段文字 7 print('this is target') 8 target;---------->执行原功能函数时,若这个函数带有@装饰器,那么会直接开始执行这个装饰器函数,并把原函数当做参数传入;注意:此处的函数的调用方法是错误的,但是使用正确的调用方法会报错,下面会解释
#最终输出
问题:我发现如果执行原函数时使用target(),会提示TypeError: 'NoneType' object is not callable错误,但是去掉括号,直接用target调用,则正常,这又是为什么呢?有没有人知道可以解答一下呢??
原理:会发生上面错误的原因,不是因为装饰器的代码写的不对,并非错误,只不过是一种不友好的装饰器,这就是为什么明明使用的是错误的调用方法,但是仍然能运行,那是因为我们把装饰器函数写成了不可被调用的函数。具体:https://www.tuicool.com/articles/FBZvya
因此!!!把上面的代码改为:
#装饰器的正确格式
def decorator(func): def dec2(): func(); print('this is decorator'); return dec2;------------->装饰器返回一个函数(而函数是可调用的对象)~上面的写法返回的对象是str对象,是不可调用的 @decorator def target(): print('this is target') target();------->此处使用正确的调用函数方法,正常运行。
3、装饰器的具体实践例子
1 #通过银行卡存取款来模拟 2 #一个函数里又实现了另外一个函数,这样实际已经违背了函数的开放封闭原则 3 4 # 以下方法实现了在不改变原函数功能,以及原函数的调用方式的情况下,拓展了原功能的功能 5 # 那么注意现在:除了check_mima(func)这个方法以外,其他方法不能再动了!!!!!!!!,这样才能真正理解到装饰器的魅力丫 6 7 #以下方法实现了在不改变原函数功能,以及原函数的调用方式的情况下,拓展了原功能的功能 8 #注意看下面优化后的函数,是不是很熟悉,内部函数使用了外部函数的变量(func),所以这是一个闭包啊 9 def check_mima(func): 10 def inner(): 11 print('密码校验中') 12 func(); 13 return inner; 14 15 @check_mima 16 def cunkuan(): 17 #check_mima(); 18 print('存款中.....'); 19 20 @check_mima 21 def qukuan(): 22 #check_mima(); 23 print('取款中...'); 24 25 26 27 #qukuan=check_mima(qukuan); 28 #cunkuan=check_mima(cunkuan); 29 30 #一个简单的方法,按下按钮1则存款,否则为取款(方法简单粗暴) 31 #在存取款操作之前加上了密码校验,但明显代码冗余还是很高 32 #可想而知,如果再加上查询、等其他业务,代码的冗余岂不是越来越高 33 34 button=1; 35 if button==1: 36 37 qukuan(); 38 else: 39 cunkuan();
##
###########################装饰器装饰有参数的函数
1 #装饰器装饰有参数的函数 2 #按照下面的写法看似没有问题,实际会返回错误:inner() takes 0 positional arguments but 1 was given 3 #看报错原因就知道,inner()函数多了一个参数。大家一定要注意一点,因为装饰器函数的返回值是inner,可以看成inner()==aa() 4 #所以inner()的输入、输出的类型!必须和aa一模一样!!!!!!!!! 5 def zhuangshi(func): 6 def inner(): 7 func(); 8 print('装饰一下'); 9 return inner; 10 @zhuangshi 11 def aa(a): 12 print('哈哈',a); 13 14 aa(2);
##因此把上面代码修改如下(注意红字部分):
1 def zhuangshi(func): 2 def inner(str): 3 func(str); 4 print('装饰一下'); 5 return inner; 6 @zhuangshi 7 def aa(a): 8 print('哈哈',a); 9 10 aa(2);
###########################装饰器装饰有返回值(return)的函数
1、因为inner函数不会有return
2、但是重点是inner()的返回值,必须必须要跟被装饰函数一样
3、所以原函数有return返回值
4、所以inner函数也要有返回值