Python基础-函数[2]

函数嵌套

在一个函数定义中再定义一个函数 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
#!/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
#!/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
#!/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

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
#!/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
#!/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)

感谢阅读,欢迎指正。

-------------本文结束感谢您的阅读-------------