Python开发【第四章】:函数剖析
转载自:https://www.cnblogs.com/lianzhilei/p/5770672.html
一、Python函数剖析
1、函数的调用顺序
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 -*- #-Author-Lian #函数错误的调用方式 def func(): #定义函数func() print ( "in the func" ) foo() #调用函数foo() func() #执行函数func() def foo(): #定义函数foo() print ( "in the foo" ) ###########打印输出########### #报错:函数foo没有定义 #NameError: name 'foo' is not defined #函数正确的调用方式 def func(): #定义函数func() print ( "in the func" ) foo() #调用函数foo() def foo(): #定义函数foo() print ( "in the foo" ) func() #执行函数func() ###########打印输出########### #in the func #in the foo |
总结:被调用函数要在执行之前被定义
2、高阶函数
满足下列条件之一就可成函数为高阶函数
-
某一函数当做参数传入另一个函数中
-
函数的返回值包含一个或多个函数
刚才调用顺序中的函数稍作修改就是一个高阶函数
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
#高阶函数 def func(): #定义函数func() print ( "in the func" ) return foo() #调用函数foo() def foo(): #定义函数foo() print ( "in the foo" ) return 100 res = func() #执行函数func() print (res) #打印函数返回值 ###########打印输出########### #in the func #in the foo #100 |
从上面的程序得知函数func的返回值为函数foo的返回值,如果foo不定义返回值的话,func的返回值默认为None;
下面我来看看更复杂的高阶函数:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
#更复杂的高阶函数 import time #调用模块time def bar(): time.sleep( 1 ) print ( "in the bar" ) def foo(func): start_time = time.time() func() end_time = time.time() print ( "func runing time is %s" % (end_time - start_time)) foo(bar) ###########打印输出########### #in the bar #func runing time is 1.0000572204589844 |
其实上面这段代码已经实现了装饰器一些功能,即在不修改bar()代码的情况下,给bar()添加了功能;但是改变了bar()调用方式
下面我们对上面的code进行下修改,不改变bar()调用方式的情况下进行功能添加
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
#更复杂的高阶函数,不改变调用方式 import time #调用模块time def bar(): time.sleep( 1 ) print ( "in the bar" ) def foo(func): start_time = time.time() print ( "in the foo" ) return func #返回bar函数的内存地址 end_time = time.time() print ( "func runing time is %s" % (end_time - start_time)) bar = foo(bar) #bar重新赋值 bar() ###########打印输出########### #in the foo #in the bar |
我们没有对bar()源码进行过修改,也没有改变bar()的调用方式,当执行bar()函数时,多加了一些功能,装饰器的一些雏形已经呈现;但是我们又发现之前添加的计算bar()执行时间的功能没有打印出来,return执行后函数就结束了。
3、内嵌函数和作用域
定义:在一个函数体内创建另外一个函数,这种函数就叫内嵌函数(基于python支持静态嵌套域)
1
2
3
4
5
6
7
8
9
10
11
|
#内嵌函数示例 def foo(): print ( "in the foo" ) def bar(): print ( "in the bar" ) bar() foo() ###########打印输出########### #in the foo #in the bar |
嵌套函数有什么用呢?我们暂时先记住这个内容
局部作用域和全局作用域的访问顺序
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
#嵌套函数变量与全部变量 x = 0 def grandpa(): x = 1 def dad(): x = 2 def son(): x = 3 print (x) son() dad() grandpa() print (x) ###########打印输出########### # 3 # 0 |
注:内嵌函数中定义的函数在全局中是无法直接执行的
原课件装饰器讲解:http://www.cnblogs.com/alex3714/articles/5765046.html
4、装饰器
定义:本质是函数(装饰其他函数),为其他函数添加附加功能的。
遵循原则:①不能修改被装饰函数的源代码
②不能修改被装饰函数的调用方式
组成:装饰器由高阶函数+内嵌函数组成
之前说了那么多其实都是了给装饰器做铺垫,回到刚才高阶函数中最后一个示例,能不能给函数加上运算时间计算?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
|
#!/usr/bin/env python # -*- coding:utf-8 -*- #-Author-Lian #装饰器 import time def timer(func): def deco(): start_time = time.time() func() #执行形参func() end_time = time.time() print ( "func runing time is %s" % (end_time - start_time)) return deco #返回函数deco的内存地址 def test1(): print ( "in the test1" ) time.sleep( 1 ) test1 = timer(test1) #重新赋值test1 此时test1=deco的内存地址 test1() #执行test1 ###########打印输出########### #in the test1 #func runing time is 1.0000572204589844 |
现在我们已经实现了装饰器的功能,但是如果test1有形参的话,上面的代码就会报错了,下面我们对代码做下修改
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
|
#!/usr/bin/env python # -*- coding:utf-8 -*- #-Author-Lian #装饰器 import time def timer(func): def deco( * args, * * kwargs): start_time = time.time() func( * args, * * kwargs) #执行形参func() end_time = time.time() print ( "func runing time is %s" % (end_time - start_time)) return deco #返回函数deco的内存地址 @timer #test1 = timer(test1) test1=deco def test1(name): print ( "in the test1 name %s" % name) time.sleep( 1 ) test1( "lzl" ) #执行test1 ###########打印输出########### #in the test1 #func runing time is 1.0000572204589844 |
上面的代码是不是觉得很完美了,呵呵,假如test1()有return返回值怎么办?你会发现最后执行test1返回值丢失了,所以要对上面的代码再完善一下了。
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 -*- #-Author-Lian #装饰器 import time def timer(func): def deco( * args, * * kwargs): start_time = time.time() res = func( * args, * * kwargs) #执行形参func() end_time = time.time() print ( "func runing time is %s" % (end_time - start_time)) return res return deco #返回函数deco的内存地址 @timer #test1 = timer(test1) test1=deco def test1(name): print ( "in the test1 name %s" % name) time.sleep( 1 ) return "return form test1" print (test1( "lzl" )) #执行test1 ###########打印输出########### #in the test1 #func runing time is 1.0000572204589844 #return form test1 |
好了现在我们探讨一个问题,函数可以被多个装饰器装饰吗?!
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 -*- #-Author-Lian #函数用多个装饰器 def w1(func): def inner( * args, * * kwargs): print ( "in the w1" ) return func( * args, * * kwargs) return inner def w2(func): def inner( * args, * * kwargs): print ( "in the w2" ) return func( * args, * * kwargs) return inner @w1 @w2 def f1( * args, * * kwargs): print ( "in the f1" ) f1() ###########打印输出########### #in the w1 #in the w2 #in the f1 |
终极版装饰器来了......
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
|
#!/usr/bin/env python # -*- coding:utf-8 -*- #-Author-Lian #终极版装饰器 def Before( * args, * * kwargs): print ( "before" ) def After( * args, * * kwargs): print ( "after" ) def Filter (before_func, after_func): def outer(main_func): def wrapper( * args, * * kwargs): before_result = before_func( * args, * * kwargs) if (before_result ! = None ): return before_result main_result = main_func( * args, * * kwargs) if (main_result ! = None ): return main_result after_result = after_func( * args, * * kwargs) if (after_result ! = None ): return after_result return wrapper return outer @Filter (Before, After) #Filter(Before,After)=outer Index=outer(Index)=wrapper def Index( * args, * * kwargs): print ( "index" ) Index() #Index() = wrapper() ###########打印输出########### #before #index #after |
5、生成器
学习生成器之前,我们先来看看什么是列表生成式
1
2
3
4
5
6
|
#列表生成式 b = [ i * 2 for i in range ( 10 )] print (b) ###########打印输出########### #[0, 2, 4, 6, 8, 10, 12, 14, 16, 18] |
通过列表生成式,我们可以直接创建一个列表。但是,受到内存限制,列表容量肯定是有限的。而且,创建一个包含100万个元素的列表,不仅占用很大的存储空间,还需要花费很长时间,如果我们仅仅需要访问前面几个元素,那后面绝大多数元素占用的空间都白白浪费了。所以,如果列表元素可以按照某种算法推算出来,那我们是否可以在循环的过程中不断推算出后续的元素呢?这样就不必创建完整的list,从而节省大量的空间。在Python中,这种调用时才会生成相应数据的机制,称为生成器:generator
要创建一个generator,有很多种方法,第一种方法很简单,只要把一个列表生成式的[]
改成()
,就创建了一个生成器
1
2
3
4
5
6
7
8
9
10
|
#生成器 l = [ i * 2 for i in range ( 10 )] print (l) g = (i * 2 for i in range ( 10 )) print (g) ###########打印输出########### #[0, 2, 4, 6, 8, 10, 12, 14, 16, 18] #<generator object <genexpr> at 0x0064AAE0> |
print(g) 打印出来的信息显示g是一个生成器,创建l
和g
的区别仅在于最外层的[]
和()
,l
是一个list,而g
是一个generator;我们可以直接打印出list的每一个元素,但我们怎么打印出generator的每一个元素呢?如果要一个一个打印出来,可以通过next()
函数获得generator的下一个返回值
1
2
3
4
5
6
7
8
9
10
11
12
13
|
#生成器next打印 print ( next (g)) #......... 不断next 打印10次 #.......... print ( next (g)) ###########打印输出########### #0 #........ #18 #Traceback (most recent call last): # File "<stdin>", line 1, in <module> #StopIteration |
我们讲过,generator保存的是算法,每次调用next(g)
,就计算出g
的下一个元素的值,直到计算到最后一个元素,没有更多的元素时,抛出StopIteration
的错误。
上面这种不断调用next(g)
实在是太变态了,正确的方法是使用for
循环,因为generator也是可迭代对象,所以,我们创建了一个generator后,基本上永远不会调用next()
,而是通过for
循环来迭代它,并且不需要关心StopIteration
的错误
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
#生成器for调用 g = (i * 2 for i in range ( 10 )) #不用担心出现StopIteration错误 for i in g: print (i) ###########打印输出########### # 0 # 2 # 4 # 6 # 8 # 10 # 12 # 14 # 16 # 18 |
generator非常强大。如果推算的算法比较复杂,用列表生成式转换的生成器无法去实现时,我们还可以用函数来实现。比如,著名的斐波拉契数列(Fibonacci)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
#函数表示斐波拉契数列 def fib( max ): n, a, b = 0 , 0 , 1 while n < max : print (b) a, b = b, a + b n + = 1 return 'done' fib( 5 ) ###########打印输出########### # 1 # 1 # 2 # 3 # 5 |
仔细观察,可以看出,fib
函数实际上是定义了斐波那契数列的推算规则,可以从第一个元素开始,推算出后续任意的元素,这种逻辑其实非常类似generator;也就是说,上面的函数和generator仅一步之遥,那我们能不能把上面的函数变成一个生成器呢?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
|
#斐波拉契数列转换为generator def fib( max ): n, a, b = 0 , 0 , 1 while n < max : #print(b) yield b a, b = b, a + b n + = 1 return 'done' print ( type (fib( 5 ))) #打印fib(5)的类型 for i in fib( 5 ): #for循环去调用 print (i) ###########打印输出########### # <class 'generator'> # 1 # 1 # 2 # 3 # 5 |
要把fib
函数变成generator,只需要把print(b)
改为yield b
就可以了,这就是定义generator的另一种方法。如果一个函数定义中包含yield
关键字,那么这个函数就不再是一个普通函数,而是一个generator
但是用for
循环调用generator时,会发现拿不到generator的return
语句的返回值,也就是return的值没有打印出来,现在我们来看看怎么去打印generator的返回值
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
|
#!/usr/bin/env python # -*- coding:utf-8 -*- #-Author-Lian #获取generator的返回值 def fib( max ): n, a, b = 0 , 0 , 1 while n < max : #print(b) yield b a, b = b, a + b n + = 1 return 'done' g = fib( 5 ) while True : try : x = next (g) print ( x) except StopIteration as e: print (e.value) break ###########打印输出########### # 1 # 1 # 2 # 3 # 5 # done |
如果想要拿到返回值,必须捕获StopIteration
错误,返回值包含在StopIteration
的value
中,关于如何捕获错误,后面的错误处理还会详细讲解。
还可通过yield实现在单线程的情况下实现并发运算的效果
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 -*- #-Author-Lian import time def consumer(name): print ( "%s 准备吃包子啦!" % name) while True : baozi = yield print ( "包子[%s]来了,被[%s]吃了!" % (baozi,name)) def producer(name): c = consumer( 'A' ) c2 = consumer( 'B' ) c.__next__() #c.__next__()等同于next(c) c2.__next__() print ( "老子开始准备做包子啦!" ) for i in range ( 10 ): time.sleep( 1 ) print ( "%s做了2个包子!" % (name)) c.send(i) c2.send(i) producer( "lzl" ) |
6、迭代器
我们已经知道,可以直接作用于for
循环的数据类型有以下2种:
-
集合数据类型,如
list
、tuple
、dict
、set
、str
等; -
生成器,包括generator和带
yield
的generator function;
定义:这些可以直接作用于for
循环的对象统称为可迭代对象:Iterable
我们可以使用isinstance()去
判断一个对象是否是Iterable
对象
1
2
3
4
5
6
7
8
9
10
11
|
#可迭代对象 from collections import Iterable print ( isinstance ([], Iterable)) # True print ( isinstance ( "abc" , Iterable)) # True print ( isinstance ((x for x in range ( 10 )), Iterable)) # True print ( isinstance ( 100 , Iterable)) # False |
我们知道生成器不但可以作用于for
循环,还可以被next()
函数不断调用并返回下一个值,直到最后抛出StopIteration
错误表示无法继续返回下一个值了
重点来了....*可以被next()
函数调用并不断返回下一个值的对象称为迭代器:Iterator
可以使用isinstance()
判断一个对象是否是Iterator
对象
1
2
3
4
5
6
7
8
9
10
11
|
#迭代器对象 from collections import Iterator print ( isinstance ([], Iterator)) # True print ( isinstance ( "abc" , Iterator)) # False print ( isinstance ((x for x in range ( 10 )), Iterator)) # True print ( isinstance ( 100 , Iterator)) # False |
由上面可知,生成器都是Iterator
对象,但list
、dict
、str
虽然是Iterable
,却不是Iterator;把
list
、dict
、str
等Iterable
变成Iterator
可以使用iter()
函数
1
2
3
4
5
|
#可迭代对象转迭代器对象 print ( isinstance ( iter ([]), Iterator)) # True print ( isinstance ( iter ( "abc" ), Iterator)) # True |
你可能会问,为什么list
、dict
、str
等数据类型是Iterable但不是
Iterator
?
这是因为Python的Iterator
对象表示的是一个数据流,Iterator对象可以被next()
函数调用并不断返回下一个数据,直到没有数据时抛出StopIteration
错误。可以把这个数据流看做是一个有序序列,但我们却不能提前知道序列的长度,只能不断通过next()
函数实现按需计算下一个数据,所以Iterator
的计算是惰性的,只有在需要返回下一个数据时它才会计算。Iterator
甚至可以表示一个无限大的数据流,例如全体自然数。而使用list是永远不可能存储全体自然数的。
小结:
- 凡是可作用于
for
循环的对象都是Iterable
类型; - 凡是可作用于
next()
函数的对象都是Iterator
类型,它们表示一个惰性计算的序列; - 集合数据类型如
list
、dict
、str
等是Iterable
但不是Iterator
,不过可以通过iter()
函数获得一个Iterator
对象;
Python的for
循环本质上就是通过不断调用next()
函数实现的,例如:
1
2
|
for x in [ 1 , 2 , 3 , 4 , 5 ]: pass |
实际上完全等价于:
1
2
3
4
5
6
7
8
9
10
|
# 首先获得Iterator对象: it = iter ([ 1 , 2 , 3 , 4 , 5 ]) # 循环: while True : try : # 获得下一个值: x = next (it) except StopIteration: # 遇到StopIteration就退出循环 break |