python--闭包、装饰器

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函数也要有返回值

 

 
posted @ 2019-02-22 11:12  littlepoemers_23ujhs  阅读(290)  评论(0编辑  收藏  举报