本篇文章将会介绍有关Python中的迭代器、迭代对象、生成器和yield关键字有关知识
首先应该明白几个问题/概念:
1.啥是迭代(器)?
使用一个循环来遍历某个东西时,这个过程本身就叫迭代。一个for循环、一个while循环或者递归函数都属于迭代。
那么迭代器就是用来迭代的器???我的理解是,迭代器相当于“指哪打哪”
的那根手指头,通俗来讲,迭代器(iterator)有时又称游标(cursor)是程序设计的软件设计模式,可在容器(container,例如链表或阵列)上遍访的接口,设计人员无需关心容器的内容。(百度百科)
你要通过下标来遍历一个数组,下标可以简单地看作一个迭代器。
2.啥是生成器?
有了迭代器的大致概念,生成器就好些理解,也是一种迭代器,但只能对其迭代一次。这是因为它们并没有把所有的值存在内存中,而是在运行时生成值。
好处显而易见了,节省大量内存空间。我们可以通过遍历来使用它们,要么用一个“for”或“while”循环,要么将它们传递给任意可以进行迭代的函数和结构。
生成器用在哪里呢? 很显然,当你不想在同一时间将所有计算出来的大量结果集分配到内存当中,生成器就显得格外有用。
然后应该明白几个Python的内置函数:
1.内置函数next()
它允许我们获取一个序列的下一个元素。序列这个概念在Python中很常见,比如range()函数所生成的序列。
2.内置函数iter()
它将根据一个可迭代对象返回一个迭代器对象。听起来很拗口,看看下面的例子吧。
3.关键字yield
可以暂时理解为return
,看完下面的例子就好体会了。
例1. yield的使用
def test():
print("starting...")
for i in range(5):
print("进入循环...")
res = yield i
print("res:", res)
print("循环结束!")
print("调用g = test():")
g = test() # 没有任何输出
for i in range(7):
print("第{}次调用print(next(g)):".format(i + 1))
try:
print(next(g))
except Exception as e:
print("异常类型",e.__class__.__name__)
print("异常描述",e.__doc__)
运行结果:
调用g = test():
第1次调用print(next(g)):
starting...
进入循环...
0
第2次调用print(next(g)):
res: None
循环结束!
进入循环...
1
第3次调用print(next(g)):
res: None
循环结束!
进入循环...
2
第4次调用print(next(g)):
res: None
循环结束!
进入循环...
3
第5次调用print(next(g)):
res: None
循环结束!
进入循环...
4
第6次调用print(next(g)):
res: None
循环结束!
异常类型 StopIteration
异常描述 Signal the end from iterator.__next__().
第7次调用print(next(g)):
异常类型 StopIteration
异常描述 Signal the end from iterator.__next__().
执行流程:
这个函数加了yield
关键字之后,其实不再是一个函数,更像一个对象了,因此g = test()
没有任何输出,不再执行这个函数。
在第一次调用test()
时,打印了test()
函数体中循环之外的语句,然后进入循环体,遇到了yield
关键字就停止了,相当于return
了当前的数字i
。
接着在第二次调用test()
时,又从上次该函数return
后停止的地方继续执行了,此时res
变量无法接收任何值,可以认为yield
返回到了函数外,对res
没有赋值。然后运行,直到第二次循环又遇到yield
关键字后返回。
到这里,有一个大致的印象:好像这个test()
函数就变成了一个序列或者类似数组的容器,每次访问这个函数时,就从上次结束的地方继续执行,然后yield
这个函数里面的值来返回(可以结合yield
的中文意思产生,产出
来理解)
接着执行第3,4,5次调用,直到第6次调用的时候,第5次yield
已经结束,退出了函数体内的循环,于是产生了异常Signal the end from iterator
,即已经到了迭代器结尾,没有next了…没法迭代了还能强迫不成?第7次调用也是一样。
over!
例2. 把字符串变成迭代器
my_string = "I like Python"
my_iter = iter(my_string)
for i in range(len(my_string)):
print(next(my_iter),end="\t")
输出结果:
I l i k e P y t h o n
如果使用print(next(my_string))
那么就会报错:TypeError: 'str' object is not an iterator
说明啥?str
类型是一个可迭代对象,而不是一个迭代器对象!这意味着它支持迭代,但我们不能直接对其进行迭代操作。因此就理解了iter()将根据一个可迭代对象返回一个迭代器对象
的含义
例3. 我们熟悉的range()函数跟迭代器的关系
print("type(range(5))=",type(range(5)))
my_range=iter(range(5))
print("type(my_range)=",type(my_range))
for i in range(5):
print(next(my_range))
输出结果:
type(range(5))= <class 'range'>
type(my_range)= <class 'range_iterator'>
0
1
2
3
4
原来,序列(range类)类似于一个列表,但也可以变成迭代器(range_iterato类)来使用!
例4. 利用yield生成斐波那契数列
def func_fibon(n):
a = 1
b = 1
for i in range(n):
yield a
temp = b
b = a + b
a = temp
for x in func_fibon(10):
print(x,end="\t")
输出结果:
1 1 2 3 5 8 13 21 34 55