初设大蟒蛇之Python函数篇
1、内嵌函数:
内嵌函数,顾名思义,就是在一个已经定义的函数内部再定义一个函数,如下简单的内嵌函数:
#!/usr/bin/env python
def func_out(): ###外部函数
print "This is func_out"
def func_in(): ###内部函数
print "This is func_in"
return func_in() ###注意,此处返回内部函数值
print func_out()
输出结果:
[student@localhost day3]$ python 1.py
This is func_out ###外部函数输出
This is func_in ###内部函数输出
None ###由于此时调用的返回值为内部函数的返回值,但内部函 数无返回值,所以输出为None
对于内嵌函数,它的作用域仅限于外部函数,在全局中除了外部函数,其他地方都不能调用该内嵌函数,否则会发生异常,如下例:
#!/usr/bin/env python
def func_out():
print "This is func_out"
def func_in():
print "This is func_in"
return func_in ###注意,此处返回一个函数对象,也就是内部函数 func_in的函数体
print func_out()
输出结果:
[student@localhost day3]$ python 1.py
This is func_out
<function func_in at 0x7f1306295a28> ###注意此输出,由于在整段程序中没有调用内部函数,所以没有内部函数的输出结果,但是有返回值,返回值为一个函数对象,也就是内部函数的地址。
对于内嵌函数,在实际当中使用还是较为常见,上面的例子只是一个简单的内嵌函数结构,并没有对其进行传参,如果对内嵌函数进行传参,则会用到python中自带的一类函数--装饰器
2、函数装饰器
2.1对于函数装饰器,先分别观察下面两段程序的不同,下面的两个函数用来统计函数的运行时间,我们用sleep函数来模仿程序运行时间:
一:正常对函数进行调用
1 #!/usr/bin/env python
2
3 import time ###加入时间函数模块
4 import datetime
5
6 def func_test(func):
7 # print "This is func_test"
8 def func_time(*arg,**kwargs):
9 print '[%s]' % time.ctime() ###输出当前时间
10 start_time = datetime.datetime.now() ###对调用函数前对时间进行统计
11 func(*arg,**kwargs) ###调用函数
12 end_time = datetime.datetime.now() ###对调用完函数进行时间统计
13 cost_time = end_time - start_time ###计算函数运行时间
14 print '[%s]' % time.ctime() ###输出当前时间
15 print 'running [%d] mins' % cost_time.total_seconds() ###将运行时间用秒输 出
16 return func_time
17
18
19 def func_test_1(): ###测试函数
20 time.sleep(3) ###暂停3秒
21
22 f=func_test(func_test_1) ###对函数进行调用
23 f()
输出结果:
[student@localhost day3]$ python 3.py
[Mon Jun 20 21:25:40 2016]
[Mon Jun 20 21:25:43 2016]
This function running [3] mins
二: 利用装饰器对函数进行调用
1 #!/usr/bin/env python
2
3 import time
4 import datetime
5
6 def func_test(func):
7 # print "This is func_test"
8 def func_time(*arg,**kwargs):
9 print '[%s]' % time.ctime()
10 start_time = datetime.datetime.now()
11 func(*arg,**kwargs)
12 end_time = datetime.datetime.now()
13 cost_time = end_time - start_time
14 print '[%s]' % time.ctime()
15 print 'This function running [%d] mins' % cost_time.total_seconds()
16 return func_time
17
18 @func_test ###装饰器调用
19 def func_test_1():
20 time.sleep(3)
21
22 func_test_1()
运行结果:
[student@localhost day3]$ python 3.py
[Mon Jun 20 21:33:20 2016]
[Mon Jun 20 21:33:23 2016]
This function running [3] mins
对于上面的两个例子,我们可以看出使用装饰器i对函数进行调用和正常调用,产生的结果是相同的,而在第二个例子中,@func_test 作为装饰器函数,该装饰器的作用相当于:func_test_1=func_test(func_test_1),但是,注意在使用装饰器函数时,所需要装饰的函数必须要紧跟在该函数后,在上面的举例中,也就是func_test_1()这个需要装饰的函数必须紧跟在@func_test后,如果中间插入其他代码,则有可能会出现异常报错,例如:
18 @func_test
19 print 'hello '
20 def func_test_1():
21 time.sleep(3)
2.1 我们在上面的函数中,装饰函数中插入一条无关语句,则会产生下面的结果:
[student@localhost day3]$ python 3.py
File "3.py", line 19
print 'hello '
^
SyntaxError: invalid syntax
产生该异常,因为装饰器无法读取装饰函数,才会提示如下错误。
2.2 此外,装饰器也可以像函数一样“堆叠”起来,如下举例:
@func_test_2
@func_test_1
def func(arg1,arg2,...):
pass
该“重叠”装饰器相当于:
def func(arg1,arg2,...):
pass
func = func_test_2(func_test_1(func))
还有,装饰器也可以带有参数,例如:
@func_test_2(arg)
@func_test_1
def func():
pass
等价于:func = func_test_2(arg) (func_test_1(func))
对于上面的例子,我们大概可以理解装饰器的作用,装饰器实际上也就是一类函数,他们可以接受函数对象,简化我们对内嵌函数的调用,我们可以用装饰器来进行:引入日志、增加计时逻辑来检测性能、给函数加入事务的能力。
2.3 对于装饰器来讲,当函数使用装饰器时,原函数的元数据便会变成装饰器内部函数的元数据,请看下面的例子:
18 @func_test
19 def func_test_1():
20 time.sleep(3)
21
22 func_test_1()
23 print func_test_1.__name__ ###我们在上面的函数中加入这句程序,将func_test_1函数的函数名进行输出,检测这个函数在利用装饰器进行调用后函数名(元数据)是否发生变化。
输出结果:
在输出结果中我们可以看到,原函数的元数据已经被装饰器函数更改。如果我们在使用装饰器函数时不需要更改元数据时,我们需要加入另一个模块functools,具体如下:
如上图,我们加入functools模块,并且在装饰器中加入第9行代码,将传入的函数元数据进行保存,而不进行修改,输出结果:
在上面的输出中,我们可以看到输出的函数名还是原来的函数名,元数据并没有进行更改。但是,这种方法相当于更改了内嵌函数的元数据,我们再对源程序进行修改,如下:
我们在内嵌函数后面加入输出内嵌函数元数据代码,第18行,输出结果如下:
在上面输出的结果中,我们可以看到内嵌函数的元数据(函数名)被更改。
3、匿名函数lambda
3.1 在python中,我们可以使用一个匿名函数来对函数进行定义:
lambda [arg1 [,arg2,arg3...] : expression
在上图中,我们简单的使用了lambda进行函数定义,但是,由于lambda创建了一个函数对象,自身并不进行保存,所以会直接返回一个函数对象,这样我们需要对lambda返回对象进行保存,如上图中,用result进行保存函数对象,由于lambda创建函数中创建了两个参数,并且返回了x+y这个表达时的值,所以我们载调用时需要进行传参。
同样,lambda函数也可以进行位置传参,也可以覆盖参数。
3.2 lambda的作用域
如同普通函数一样,lambda函数也同样遵循固定的作用域范围
对于作用域的范围,在上面两幅图中,我们可以看到,lambda函数的使用作用域和普通函数相同。
4、生成器
4.1 在python中,生成器的作用类似于迭代器,不过生成器使用另一种方式来运作,当到达一个真正的返回或者函数结束没有更多的返回值时,就会退出(当调用next(),出现StopIteration异常时,就会退出)。生成器在python中可以当作无限迭代来使用,我们来看个例子:
在下图中,我们定义一个生成器,打印10以内的奇数:
在上面的例子中,利用for循环,相当于进行了多次next()操作。
生成器中,我们不仅可以使用next()将生成器返回值进行输出,也可以对生成器进行send()传入数据,在使用send()传入参数时,默认将传入的参数进行next()操作,如下举例:
在上图中的例子中,我们对生成器传入参数,如果传入字符为’q’,则退出。注意,在上面的程序中,recv = yield value 这句代码相当于,先对value进行一次中断,然后等待外界的参数传入,并且对recv进行赋值,在这里,由于由于第一次运行时没有对外界请求传入参数,所以我们在第一次send传参时需要传入一个空值,否则会有异常。而当最后一次传入参数为’q’时,由于生成器在请求完,默认进行next()输出时,函数已经退出,所以,无法正常输出,所以在上面的例子中,输出结果会报出异常。
我们来做一个简单的对话生成器:
1 #!/usr/bin/env python
2
3 def gen():
4 value = 0
5 while True:
6 recv = yield value
7 if recv == 'hello':
8 value = 'hello~~~~'
9 elif recv == 'age':
10 value = '18'
11 else:
12 value = 'what do say?'
13 recv = value
14
15 g=gen()
16 g.send(None)
17 while True:
18 text=raw_input("please input: ")
19 if text == 'bye':
20 print "bye~~~"
21 break
22 print g.send(text)
输出结果: