迭代 装饰器 闭包
迭代器:
包含yield表达式的函数是特殊的函数,叫做生成器函数(generator function),被调用时将返回一个迭代器(iterator),调用时可以使用next或send(msg)。它的用法与return相似,区别在于它会记住上次迭代的状态,继续执行。
send(msg)与next()的区别在于send可以传递参数给yield表达式,这时传递的参数会作为yield表达式的值,而yield的参数是返回给调用者的值。初始调用时必须先next()或send(None),否则会报错。
举个例子:
首先生成一个迭代器f,f.next()会使生成器函数执行到yield,生成一个值然后挂起。
然后f.next()或f.send(msg)会在生成器函数内部返回值,执行到下一个yield,生成值后挂起
然后f.next()或f.send(msg)会在生成器函数内部返回值,意图执行到下一个yield,但是后面没有yield了,所以抛出异常。
使用yield可以有效简化代码,并减少空间浪费。
举个简单例子:列表中的每个元素+1
传统写法:
Python代码
- def addlist(alist):
- r = []
- for i in alist:
- r.append(i+1)
- return r
yield写法:
Python代码
- def addlist(alist):
- for i in alist:
- yield i + 1
当然对于这种简单的问题:
Python代码
- [i+1 for i in alist]
1、定义:假设我们要增强一个函数的功能,比如,在函数调用前后自动打印日志,但又不希望改变这个函数的源代码,这种在代码运行期间动态增加功能且又不改变源代码的方式,成为装饰器(Decorator)。本质上,decorator就是一个返回函数的高阶函数
2、示例:
如上图中的原函数为index(),我们通过装饰器为其增加了一个计算运行时间的功能,但是没有改变源代码,这就是为其增加了一个装饰器,装饰器的功能就是计时。
关键点:@的语法,@timmer等同于进行了如下操作:index=timmer(index),函数名+()就是调用函数,一定要记住!!好多地方想不通原因就是在这里!
思想就是把部内函数func()换成被装饰函数index()然后再运行闭包函数就好了(可能说的有点简单)
3、复杂一点的例子,代参数的,并且是用于多个参数不确定的函数的装饰器
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
|
import time def timmer(func): def wrapper ( * args, * * kwargs): ##注意注意*args和**kwargs start_time = time.time() func( * args, * * kwargs) ##注意注意*args和**kwargs stop_time = time.time() print ( "run time is %s" % (stop_time - start_time)) return wrapper @timmer ##注意注意位于被装饰函数最上方,且单独占一行 def home(name): time.sleep( 2 ) print ( "welcome to %s home page" % name) @timmer def auth(name,password): print (name,password) @timmer def tell(): print ( '=======' ) home( 'dragon' ) auth( 'egon' , '123' ) tell() |
4、装饰用户认证功能
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
|
#现在是对index()函数增加了用户认证功能,源代码没有变<br>def auth2(auth_type): def auth(func): def wrapper( * args, * * kwargs): if auth_type = = 'file' : name = input ( 'username:' ) password = input ( 'password:' ) if name = = 'zhejiangf4' and password = = 'sbasb' : print ( 'auth successful' ) res = func( * args, * * kwargs) return res else : print ( 'auth error' ) elif auth_type = = "sql" : print ( "还他妈不会玩" ) return wrapper return auth @auth2 (auth_type = "file" ) #==>@auth==>index=auth(index),所以auth2作用就是传了一个值 def index(): print ( 'Welcome to index page!' ) index() |
5、再给上面的函数加一个时间模块
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
|
import time def timmer(func): def wrapper(): start_time = time.time() func() stop_time = time.time() print (stop_time - start_time) return wrapper def auth2(auth_type): def auth(func): def wrapper( * args, * * kwargs): if auth_type = = 'file' : name = input ( 'username:' ) password = input ( 'password:' ) if name = = 'zhejiangf4' and password = = 'sbasb' : print ( 'auth successful' ) res = func( * args, * * kwargs) return res else : print ( 'auth error' ) elif auth_type = = "sql" : print ( "还他妈不会玩" ) return wrapper return auth @timmer @auth2 (auth_type = "file" ) # def index(): print ( 'Welcome to index page!' ) index() |
上面函数的运行原理需要细说一下
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
|
#先把大体的概念说一下: # def aaa():#装饰函数 # @aaa # def func():#被装饰函数 # pass # # func=aaa(func) # @ccc # @bbb # @aaa # def func(): # pass # # func=ccc(bbb(aaa(func))) # # @ccc('c') # @bbb('b') # @aaa('a') # def func(): # pass # # func=ccc('c')(bbb('b')(aaa('a')(func))) #上边的例子是下边这个规律 #founc=bbb(aaa('a')(func)) #index=timmer(auth2(auth_type="list")(func)) #index=timmer(auth(func)) #index=timmer(wrapper_dixia) #index=wrapper_shangbian #index()=wrapper_shangbian() #index()=wrapper_dixia() |
6、eval函数:会把字符串里面的东西读出来执行,结果必须要赋值,不然砸电脑
1
2
3
4
5
6
7
8
9
|
m = input ( ">>:" ) m = eval (m) print (m, type (m)) >>:{ "name" : "agon" } { 'name' : 'agon' } < class 'dict' > >>:[ "agon" ] [ 'agon' ] < class 'list' > >>: print ( 123 ) |
7、给认证装饰器增加一个登陆后再次调用是免认证的功能(字典,只在内存中能行)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
|
import time current_login = { 'name' : None , 'login' : False } #建立一个字典,字典存储登录状态 def timmer(func): def wrapper(): start_time = time.time() func() stop_time = time.time() print ( 'run time is %s' % (stop_time - start_time)) return wrapper def auth2(auth_type = 'file' ): def auth(func): def wrapper( * args, * * kwargs): if current_login[ 'name' ] and current_login[ 'login' ]: #判断状态是否被激活,若激活直接执行函数结束 res = func( * args, * * kwargs) return res if auth_type = = 'file' : name = input ( 'username:' ) password = input ( 'password:' ) if name = = 'zhejiangF4' and password = = 'sb945' : print ( 'auth successful' ) res = func( * args, * * kwargs) current_login[ 'name' ] = name #存储登录状态 current_login[ 'login' ] = True return res else : print ( 'auth error' ) elif auth_type = = "sql" : print ( "haibuhui" ) return wrapper return auth @timmer @auth2 (auth_type = "file" ) def index(): print ( 'welcome to index page' ) @auth2 ( "file" ) def home(): print ( "welcome to home page" ) index() home() #第一次执行index()函数是需要登录认证,但第二次执行home时就不需要再认证了 |
8、怎样在增加装饰器后还可以打印原函数的注释内容
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
|
import time from functools import wraps #从函数工具中调用wraps模块 def timmer(func): @wraps (func) #它就可以让你打印出的index.__doc__编程原函数的"dashabi"而不是wrapper函数的"000" def wrapper( * args, * * kwargs): '000' start_time = time.time() res = func( * args, * * kwargs) stop_time = time.time() print ( 'run time is %s' % (stop_time - start_time)) return res return wrapper @timmer def index(): "dashabi" print ( "from index" ) index() print (index.__doc__) |
闭包
闭包并不是什么新奇的概念,它早在高级语言开始发展的年代就产生了。闭包(Closure)是词法闭包(Lexical Closure)的简称。对闭包的具体定义有很多种说法,这些说法大体可以分为两类:
一种说法认为闭包是符合一定条件的函数,比如参考资源中这样定义闭包:闭包是在其词法上下文中引用了自由变量的函数。
另一种说法认为闭包是由函数和与其相关的引用环境组合而成的实体。比如参考资源中就有这样的的定义:在实现深约束时,需要创建一个能显式表示引用环境的东西,并将它与相关的子程序捆绑在一起,这样捆绑起来的整体被称为闭包。
就像这样:
#python 中的闭包 ... def func(data): ... count = [data] ... def wrap(): ... count[0] += 1 ... return count[0] ... return wrap ... ... a = func(1) >>> a() 5: 2 >>> a() 6: 3 def func(x): ... return lambda y :y+x >>> b = func(1) >>> b(1) 7: 2 >>> b(2) 8: 3 >>> print b #这里b是个function 在ruby中是proc <function <lambda> at 0x01AC68F0> def addx(x): ... def adder (y): return x + y ... return adder >>> add8 = addx(8) >>> add8(8) 9: 16
简单说,闭包就是根据不同的配置信息得到不同的结果
python实例
看概念总是让人摸不着头脑,看几个python小例子就会了
例1
def make_adder(addend): def adder(augend): return augend + addend return adder p = make_adder(23) q = make_adder(44) print p(100) print q(100)
运行结果:
123 144
分析一下:
我们发现,make_adder是一个函数,包括一个参数addend,比较特殊的地方是这个函数里面又定义了一个新函数,这个新函数里面的一个变量正好是外部make_adder的参数.也就是说,外部传递过来的addend参数已经和adder函数绑定到一起了,形成了一个新函数,我们可以把addend看做新函数的一个配置信息,配置信息不同,函数的功能就不一样了,也就是能得到定制之后的函数.
再看看运行结果,我们发现,虽然p和q都是make_adder生成的,但是因为配置参数不同,后面再执行相同参数的函数后得到了不同的结果.这就是闭包.
例2
def hellocounter (name): count=[0] def counter(): count[0]+=1 print 'Hello,',name,',',str(count[0])+' access!' return counter hello = hellocounter('ma6174') hello() hello() hello()
执行结果
Hello, ysisl , 1 access! Hello, ysisl , 2 access! Hello, ysisl , 3 access!
分析一下
这个程序比较有趣,我们可以把这个程序看做统计一个函数调用次数的函数.count[0]可以看做一个计数器,没执行一次hello函数,count[0]的值就加1。也许你会有疑问:为什么不直接写count而用一个列表?这是python2的一个bug,如果不用列表的话,会报这样一个错误:
UnboundLocalError: local variable 'count' referenced before assignment.
什么意思?就是说conut这个变量你没有定义就直接引用了,我不知道这是个什么东西,程序就崩溃了.于是,再python3里面,引入了一个关键字:nonlocal,这个关键字是干什么的?就是告诉python程序,我的这个count变量是再外部定义的,你去外面找吧.然后python就去外层函数找,然后就找到了count=0这个定义和赋值,程序就能正常执行了.
python3 代码
def hellocounter (name): count=0 def counter(): nonlocal count count+=1 print 'Hello,',name,',',str(count[0])+' access!' return counter hello = hellocounter('ma6174') hello() hello() hello()
例3
def makebold(fn): def wrapped(): return "<b>" + fn() + "</b>" return wrapped def makeitalic(fn): def wrapped(): return "<i>" + fn() + "</i>" return wrapped @makebold @makeitalic def hello(): return "hello world" print hello()
执行结果
<b><i>hello world</i></b>
简单分析
怎么样?这个程序熟悉吗?这不是传说的的装饰器吗?对,这就是装饰器,其实,装饰器就是一种闭包,我们再回想一下装饰器的概念:对函数(参数,返回值等)进行加工处理,生成一个功能增强版的一个函数。再看看闭包的概念,这个增强版的函数不就是我们配置之后的函数吗?区别在于,装饰器的参数是一个函数或类,专门对类或函数进行加工处理。
python里面的好多高级功能,比如装饰器,生成器,列表推到,闭包,匿名函数等,开发中用一下,可能会达到事半功倍的效果!
以上三篇博文分别转载于
http://blog.csdn.net/orangleliu/article/details/8752058
http://www.cnblogs.com/wuyongqiang/p/6690004.html
http://www.jb51.net/article/70897.htm