[ Python ] 迭代对象、迭代器、生成器
1. 容器(container)
容器是一种把多个元素组织在一起的数据结构,容器中的元素可以逐个的迭代获取,可以用 in / not in 关键字判断元素是否包含在容器中。通常这类数据结构把所有元素存储在内存中。比如:list, set, tuples都是容器对象
>>> 1 in [1, 2, 3] True >>> 4 not in [1,2,3] True >>> 1 in (1,2,3) True >>> 4 not in (1,2,3) True >>> 1 in {1,2,3} True >>> 4 not in {1,2,3} True
2. 可迭代对象(iterable)
什么的迭代?
迭代就是可以被 for 循环遍历, 在python中一切皆对象,可以被 for 循环迭代的对象就叫 可迭代对象;
>>> for i in [1,2,3]: ... print(i) ... 1 2 3 >>> for i in (1,2,3): ... print(i) ... 1 2 3 >>> for i in {'a': 1, 'b': 2, 'c': 3}: ... print(i) ... a c b
可迭代对象并不是指某种具体的数据类型,list 是可迭代对象,dict是可迭代对象,set也是可迭代对象。
3. 迭代器(iterator)
可迭代对象是实现了 __iter__() 方法的对象,而迭代器(iterator)则是实现了 __iter__() 和 __next()__ 方法的对象,可以显示的获取下一个元素,这种可以被 next 调用并不断返回下一个值的对象称为迭代器;
iterator是一个带状态的对象,能在调用next()方法的时候返回容器中的下一个值,任何实现了__iter__和__next__()方法的对象都是迭代器
__iter__返回迭代器自身,__next__返回容器中的下一个值,如果容器中没有更多的元素了,则抛出StopIteration异常。所以迭代器是每次询问要下一个值的时候返回
迭代器一定是可迭代对象,反过来则不一定成立。
>>> l1 = ['a', 'b', 'c'] >>> l_iter = l1.__iter__() # 可迭代对象通过调用 __iter__方法生成一个迭代器 >>> l_iter <list_iterator object at 0x000001EA16975710> # 迭代器通过 __next__方法返回一个值 >>> l_iter.__next__() 'a' >>> l_iter.__next__() 'b' >>> l_iter.__next__() 'c' >>> l_iter.__next__() Traceback (most recent call last): File "<stdin>", line 1, in <module> StopIteration
迭代器是一个惰性的计算,等到有人调用的时候才返回值,没调用的时候就处于休眠状态等待下一次调用。
4. 生成器(generator)
generator 是一种数据类型,这种数据类型自动实现了迭代器协议,所以生成器就是可迭代对象;
生成器一定是迭代器,反之则不成立。
当在内存中存储一个很大的列表时,需要消耗大量的内存,如果列表元素可以按照某种算法推算出来,那我们就可以通过循环的过程不断推算出后续的元素,这样就不必创建完成的list, 从而节约了大量的空间。在python中,这种一边循环一边计算的机制,称为生成器(generator)
创建一个生成器有两种方式:
(1)把一个列表生成式的[]改成()
>>> l1 = ['a', 'b', 'c'] >>> l = [x for x in range(10)] >>> l [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] >>> g = (x for x in range(10)) >>> g <generator object <genexpr> at 0x000001EA16972308>
(2)通过yield 关键字
def fib(max): n, a, b = 0, 0, 1 while n < max: yield b a, b = b, a + b n += 1 f = fib(5) print(list(f)) # 执行结果: # [1, 1, 2, 3, 5]
fib就是一个普通的python函数,特殊的地方在于函数体中没有 return关键字,函数的返回值是一个生成器对象。此时函数体中的代码并不会执行,保存的是一个算法。
可以通过遍历迭代器的方式来遍历生成器。
5. 总结
(1)容器是一系列元素的集合:str, list, set, dict, file, sockets对象都可以看做容器,容器是可以被迭代的,因此他们称为可迭代对象
(2)可迭代对象实现了__iter__方法,该方法返回一个迭代器对象
(3)迭代器持有一个内部状态的字段,用于记录下一次迭代返回值,它实现了__next__和__iter__方法,迭代器不会一次性把所有元素加载到内存,而是需要的时候返回结果。
(4)生成器是一种特殊的迭代器,它的返回值不是通过return而是通过yield.