函数嵌套
在一个函数定义中再定义一个函数 def outfunc(): def infunc(): expression
返回一个函数:
格式:
1 2 3 4 | def outfunc(): def infunc(): expression return infunc |
eg:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | #!/usr/bin/env python # coding=utf-8 def outfunc(): print 'this is calling outfunc.....' def infunc(): print 'this is calling infunc....' return infunc f = outfunc() #这里返回infunc函数 f() =========结果========== root@tonglele /code/Python/function/test # python code1.py this is calling outfunc..... this is calling infunc.... |
此时我们举一个例子:例如,我们现在想测试一个函数的运行时间,但又不破坏这个函数,不给这个函数增加代码,我们该怎样做?
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 | #!/usr/bin/env python # coding=utf-8 import time import datetime def func(arg): time.sleep(arg) def timeIt(func): def warp(arg): start = datetime.datetime.now() func(arg) end = datetime.datetime.now() cost = end - start print "execute %s spend %s" % (func.__name__,cost.total_seconds()) return warp new_func = timeIt(func) #此时timeIt返回一个新的函数warp new_func(3) #调用新的函数,并传值进去 =========运行结果=========== root@tonglele /code/Python/function/test # python code2.py execute func spend 3.003061 |
这样我们便使用了一种非入侵的方式包装了这个函数,并且增加了我们需要的功能,这就是Python中的装饰器。
装饰器
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 | #!/usr/bin/env python # coding=utf-8 import time import datetime def timeIt(func): def warp(arg): start = datetime.datetime.now() func(arg) end = datetime.datetime.now() cost = end - start print "execute %s spend %s" % (func.__name__,cost.total_seconds()) return warp @timeIt #此时,func函数被timeIt装饰器包装 def func(arg): print func.__name__ time.sleep(arg) func(3) ============运行结果========= root@tonglele /code/Python/function/test # python code2.py warp #此时func.__name__返回的名字不再是‘func’了,而是被装饰器改变成了warp。如果想要保留原来的__name__,__doc__等元信息,需要做如下修改。 execute func spend 3.003148 |
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 | #!/usr/bin/env python # coding=utf-8 import time import datetime import functools def timeIt(func): @functools.wraps(func) #增加这一行,将原函数作为值传进去,表示将原函数的__name__,__moudle__,__doc__等信息更新到装饰器里 def warp(arg): start = datetime.datetime.now() func(arg) end = datetime.datetime.now() cost = end - start print "execute %s spend %s" % (func.__name__,cost.total_seconds()) return warp @timeIt def func(arg): print func.__name__ time.sleep(arg) func(3) |
lambda匿名函数
Python使用lambda关键字创造匿名函数。匿名,即不再用def语句这样的标准形式定义一个函数。
这种语句的目的是由于性能原因,在调用时绕过函数的栈分配
语法为:lambda [arg1[,arg2,...argN]]:expression
其中,参数是可选的,如果使用参数的话,参数通常也会在表达式中出现
lambda语句的使用方法(无参数)
1 2 3 4 5 | >>> lambda:1 <function <lambda> at 0x7f02d5f3f398> >>> f = lambda:1 #lambda会返回一个函数,将该函数赋值给变量f >>> f() 1 |
lambda语句的使用方法(有参数)
1 2 3 4 5 6 7 8 9 10 11 12 13 | >>> f = lambda x,y:x+y >>> f(2,3) 5 >>> f = lambda x,y = 10:x+y #带默认值参数 >>> f(2) 12 >>> f(19,20) 39 >>> f = lambda *x:map(lambda x:x + 10,x) #可变位置参数 >>> f(1,2,3,4) [11, 12, 13, 14] >>> f(*[2,3,4,5,6]) [12, 13, 14, 15, 16] |
一个计算器的例子
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | #!/usr/bin/env python # coding=utf-8 func = { "add":lambda x,y : x + y, "sub":lambda x,y : x - y, "mul":lambda x,y : x * y, "div":lambda x,y : x / y } print func["add"](3,4) print func["sub"](3,4) print func["mul"](3,4) print func["div"](3,4) =============运行结果====== root@tonglele /code/Python/function/test # python code3.py 7 -1 12 0 |
generator生成器
与列表解析语法类似,只不过把列表解析的[]换成了()
生成器表达式能做的事,列表解析基本都能处理,只不过在需要处理的序列比较大时,列表解析比较费内存
简单的生成器表达示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | >>> gen = (x ** 2 for x in range(5)) >>> gen <generator object <genexpr> at 0x7fc2e26b3690> >>> >>> gen.next() 0 >>> gen.next() 1 >>> gen.next() 4 >>> gen.next() 9 >>> gen.next() 16 >>> gen.next() Traceback (most recent call last): File "<stdin>", line 1, in <module> StopIteration >>> |
生成器表达式并不能实现较为复杂的功能,所以我们一般都使用生成器函数
生成器函数
在函数中如果出现了yield关键字,那么该函数就不再是普通函数,而是生成器函数
生成器函数可以生成一个无限的序列,列表解析是无法这么做的
yield的作用就是把一个函数变成一个generator,带有yield的函数不再是一个普通函数,Python解释器会将其视为一个
generator。
eg1:简单使用生成器函数 :
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 | #!/usr/bin/env python # coding=utf-8 def odd(): n = 1 while True: yield n n += 2 od = odd() count = 0 for i in od: if count > 5: break print i count += 1 =========运算结果======= root@tonglele /code/Python/function/test # python code4.py 1 3 5 7 9 11 |
在for 循环执行的时候,每次都会执行odd 函数内的代码。
执行到yield n 时,将n 的值返回出去,此时函数内的代码不再往下执行, 挂起状态,但会保存函内变量的相关信息
下次迭代的时,从yield n 的下一条语句,n += 2 开始执行,
这个时候n 的值是之前保留的值,函数体内的while 循环继续执行,当再次执行到yield n 的时候,将n 返回出去,此时的n 是已经计算过n+=2 了
看起来就好像一个函数在正常执行的过程中被 yield 中断了数次,每次中断都会通过 yield 返回当前的迭代值。
yeild与return
在一个生成器中,如果没有return,则默认执行到函数结束时返回stopiteration;
1 2 3 4 5 6 7 8 9 10 | >>> def gen(): #定义一个生成器 ... yield 1 ... >>> g = gen() >>> next(g) #第一次使用next(g)时,会在执行完yield语句后挂起,此时程序并没有执行结束 1 >>> next(g) #此时,程序从yield语句的下一条语句开始执行,发现已经到了结尾,所以抛出StopIteration异常。 Traceback (most recent call last): File "<stdin>", line 1, in <module> StopIteration |
如果在执行的过程中遇到了return,则直接抛出StopIteration终止迭代
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 | >>> def gen(): ... count = 0 ... while count < 3: ... yield count ... count += 1 ... return ... >>> g = gen() >>> next(g) #第一次执行next时,程序停留在执行完yield count之后的位置 0 >>> next(g) #当这次执行时,先执行count += 1,再向下执行时,遇到了return,所以抛出StopIteration 异常,退出 Traceback (most recent call last): File "<stdin>", line 1, in <module> StopIteration >>> def gen(): ... count = 0 ... while count < 3: ... yield count ... count += 1 ... >>> g = gen() >>> next(g) 0 >>> next(g) 1 >>> next(g) 2 >>> next(g) Traceback (most recent call last): File "<stdin>", line 1, in <module> StopIteration |
close()
手动关闭生成器函数,后面的调用会直接返回StopIteration 异常。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | >>> def gen(): ... count = 0 ... while count < 3: ... yield count ... count += 1 ... >>> g = gen() >>> next(g) 0 >>> g.close() >>> next(g) Traceback (most recent call last): File "<stdin>", line 1, in <module> StopIteration |
send()
生成器函数最大的特点就是可以接受外部传入的一个变量,并根据变量内容计算结果后返回。这是生成器函数最难理解也是最重要的地方。后面的协程部分会经常用到
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 | #!/usr/bin/env python #coding=utf-8 def gen(): value = 0 while True: receive = yield value if receive == 'e': break value = 'got : %s' % receive g = gen() print g.send(None) print g.send('hello') print g.send(8989) print g.send('e') print g.send('world') ===========运行结果=========== root@tonglele /code/Python/function/test # python code6.py 0 got : hello got : 8989 Traceback (most recent call last): File "code6.py", line 16, in <module> print g.send('e') StopIteration ###执行流程: 1. 通过g.send(None)或者next(g)可以启动生成器函数,并执行到第一个yield语句结束的位置。此时,执行完了yield语句,但是没有给receive赋值。yield value会输出初始值0注意:在启动生成器函数时只能send(None),如果试图输入其它的值都会得到错误提示信息。 2. 通过g.send(‘hello’),会传入hello,并赋值给receive,然后计算出value的值,并回到while头部,执行yield value语句有停止。此时yield value会输出”got: hello”,然后挂起。 3. 通过g.send(8989),会重复第2步,最后输出结果为”got: 8989′′ 4. 当我们g.send(‘e’)时,程序会执行break然后推出循环,最后整个函数执行完毕,所以会得到StopIteration异常。 |
总结:
1、 按照鸭子模型理论,生成器就是一种迭代器,可以使用for进行迭代。
2、第一次执行next(generator)时,会执行完yield语句后程序进行挂起,所有的参数和状态会进行保存。再一次执行next(generator)时,会从挂起的状态开
始往后执行。在遇到程序的结尾或者遇到StopIteration时,循环结束。
3、 可以通过generator.send(arg)来传入参数,这是协程模型。
4、 next()等价于send(None)
感谢阅读,欢迎指正。