可迭代对象是迭代器、生成器和装饰器的基础。
简单来说,可以使用for来循环遍历的对象就是可迭代对象。比如常见的list、set和dict。
我们来看一个例子:
from collections import Iterable print(isinstance('abcddddd', Iterable)) # str是否可迭代 print(isinstance([1,2,3,4,5,6], Iterable)) # list是否可迭代 print(isinstance(12345678, Iterable)) # 整数是否可迭代 -------------结果如下---------------- True True False当对所有的可迭代对象调用 dir() 方法时,会发现他们都实现了 iter 方法。这样就可以通过 iter(object) 来返回一个迭代器。
x = [1, 2, 3] y = iter(x) print(type(x)) print(type(y)) ------------结果如下------------ <class 'list'> <class 'list_iterator'>可以看到调用iter()之后,变成了一个list_iterator的对象。可以发现增加了一个__next__方法。所有实现了__iter__和__next__两个方法的对象,都是迭代器。
迭代器是带状态的对象,它会记录当前迭代所在的位置,以方便下次迭代的时候获取正确的元素。__iter__返回迭代器自身,__next__返回容器中的下一个值,如果容器中没有更多元素了,则抛出Stoplteration异常。
x = [1, 2, 3] y = iter(x) print(next(y)) print(next(y)) print(next(y)) print(next(y)) ----------结果如下---------- 1 2 3 Traceback (most recent call last): File "/Users/rocky/Desktop/test.py", line 6, in <module> print(next(y)) StopIteration如何判断对象是否是迭代器,和判断是否是可迭代对象的方法差不多,只要把 Iterable 换成 Iterator。
Python的for循环本质上就是通过不断调用next()函数实现的,举个栗子,下面的代码
x = [1, 2, 3] for elem in x: ...也就是先将可迭代对象转化为Iterator,再去迭代。应该是处于对内存的节省考虑。因为迭代器只有在我们调用 next() 才会实际计算下一个值。
itertools 库提供了很多常见迭代器的使用。
>>> from itertools import count # 计数器 >>> counter = count(start=13) >>> next(counter) 13 >>> next(counter) 14无限循环序列:
>>> from itertools import cycle >>> colors = cycle(['red', 'white', 'blue']) >>> next(colors) 'red' >>> next(colors) 'white' >>> next(colors) 'blue' >>> next(colors) 'red'我们创建列表的时候,受到内存限制,容量肯定是有限的,而且不可能全部给他一次枚举出来。上面的思路是使用列表生成式,但是它有一个致命的缺点就是定义即生成,非常的浪费空间和效率。
如果列表元素可以按照某种算法推算出来,那我们可以在循环的过程中不断推算出后续的元素,这样就不必创建完整的list,从而节省大量的空间。在Python中,这种一边循环一边计算的机制,称为生成器:generator。
要创建一个generator,最简单的方法是改造列表生成式:
a = [x*x for x in range(10)] print(a) b = (x * x for x in range(10)) print(b) --------结果如下-------------- [0, 1, 4, 9, 16, 25, 36, 49, 64, 81] <generator object <genexpr> at 0x10557da50>还有一个方法是生成器函数,同样是通过def定义,然后通过yield来支持迭代器协议,所以比迭代器写起来更简单。
def spam(): yield"first" yield"second" yield"third" for x in spam(): print(x) -------结果如下--------- first second third进行函数调用的时候,返回一个生成器对象。在使用next()调用的时候,遇到yield就返回,记录此时的函数调用位置,下次调用next()时,从断点处开始。
我们完全可以像使用 iterator 一样使用 generator ,当然除了定义。定义一个iterator,你需要分别实现 iter() 方法和 next() 方法,但 generator 只需要一个小小的yield 。
generator 还有 send() 和 close() 方法,都是只能在next()调用之后,生成器出去挂起状态时才能使用的。
python 是支持协程的,也就是微线程,就是通过 generator 来实现的。配合 generator 我们可以自定义函数的调用层次关系从而自己来调度线程。