Python迭代器,生成器和协程
基础概念
在学习Python协程之前首先需要了解Python生成器的概念,而生成器又是一种特殊的迭代器,所以从迭代器开始学习。
Python迭代器(Iterator)
首先了解可迭代对象(interable), 具体来说就是遵循了可迭代协议的对象,比如查看Python list内置类的源码可以发现它实现了iter()函数,所以list是一个可迭代对象,当然还有dict, str, set等等。
可迭代协议:含iter()方法。且可迭代对象中的iter()方法返回的是一个对应的迭代器。(如list对应的迭代器就是list_iterator)
而迭代器与可迭代对象不同的是,迭代器对象不仅需要实现iter()方法,它还需要实现next()方法,即迭代器协议,事实上任何实现了iter()和next()方法的类对象都可以是迭代器对象。
迭代器协议:
- 含iter()方法。且方法返回的Iterator对象本身
- 含next()方法,每当next()方法被调用,返回下一个值,直到没有值可以访问,这个时候会抛出stopinteration的异常。
此外迭代器含有两个基本的方法iter()和next(), iter()方法的作用是返回一个迭代器对象,当我们使用迭代器的next()方法显式获取元素的时候,迭代器对象会返回当前游标所指向的元素,并且向后移动游标位置,类似于C语言中的指针。如果没有元素可以获取,迭代器会抛出StopIteration异常,其实我们使用for…in…语句遍历迭代器元素的时候,for循环自动帮我们捕获了StopIteration异常。
问题来了,我们知道迭代器对象(例如:list)和可迭代对象都可以用for…in…语句调用,那么它们的区别是什么呢?举一个简单的栗子:
a = [1, 2, 3, 4]
print('Start iterating list a.')
print(type(a))
for i in a:
print(i)
print('After iteration, a is', a)
b = iter(a)
print('\nStart iterating iterator b.')
print(type(b))
for i in b:
print(i)
print('After iteration, b is', list(b))
for i in b:
print(i)
print('After iteration, b is', list(b))
# 运行结果:
# Start iterating list a.
# <class 'list'>
# 1
# 2
# 3
# 4
# After iteration, a is [1, 2, 3, 4]
# Start iterating iterator b.
# <class 'list_iterator'>
# 1
# 2
# 3
# 4
# After iteration, b is []
# After iteration, b is []
很明显,从运行结果可以看出,迭代器对象遍历元素是消耗型的,遍历结束之后,迭代器中的元素集为空,这类似于队列的出队操作,但实际上的原因是内存指针指向最后一个元素的内存地址后再继续向后移动指针就没有更多的元素,所以迭代器是一个一次性产品,如果想重复使用,就需要使用list()函数将迭代器转换为一个可迭代对象。
此外,迭代器对象和可迭代对象还有很重要的一个不同就是,可迭代对象在生成时将所有元素存入内存,然后迭代器对象只存储可迭代对象的第一个元素的地址,在遍历元素的时候,next()函数会移动内存指针,读取下一个元素。所以迭代器极大地节省了内存。更直观的例子如下:
import sys
a = [i for i in range(1, 10000000)]
print('The size of list a is', sys.getsizeof(a))
b = iter(a)
print('The size of iterator object b is', sys.getsizeof(b))
# 运行结果:
# The size of list a is 81528056
# The size of iterator object b is 56
Python生成器(generator)
生成器,顾名思义,就是数据的生成者,是一种特殊的迭代器,即它也实现了迭代协议,iter()和next()函数。由上一段落,可知,迭代器保存的是可迭代对象某个元素的地址,但是生成器保存的是生成新元素所使用的算法,换句话说生成器的元素都是动态生成的。在Python中,有两种创建生成器的方式,生成器表达式和生成器函数。
生成器表达式:
生成器表达式类似于可迭代对象的创建式,只是将[]换成()a = [i for i in range(1, 10)] print(type(a)) b = (i for i in range(1,