Python的迭代器集成在语言之中,迭代器和生成器是Python中很重要的用法,本文将深刻了解迭代器和生成器。python
首先,咱们都知道for循环是一个基础迭代操做,大多数的容器对象均可以使用for循环,那么,咱们从for循环开始:git
你有没有想过,for循环的内部实现原理呢?github
其实,在Python中,for循环是对迭代器进行迭代的语法糖,内部运行机理就是:首先底层对循环对象实现迭代器包装(调用容器对象的__iter__
方法)返回一个迭代器对象,每循环一步,就调用一次迭代器对象的__next__
方法,直到循环结束时,自动处理StopIteration这个异常。函数
对于像list,dict等容器对象而言,均可以使用for循环,可是它们并非迭代器,它们属于可迭代对象。性能
什么是可迭代对象呢?code
最简单的解释:实现了迭代方法能够被迭代的对象,能够使用isinstance()方法进行判断。协程
举个例子:对象
In [1]: from collections import Iterable, Iterator In [2]: a = [1, 2, 3] In [3]: isinstance(a, Iterable) Out[3]: True In [4]: b = a.__iter__() In [5]: isinstance(b, Iterator) Out[5]: True
可迭代对象实现了__iter__
方法,该方法返回一个迭代器对象。ip
以上,能够看到,在迭代过程当中,实际调用了迭代器的__next__
方法进行迭代。内存
那么,什么是迭代器?
实现了迭代器协议的对象就是迭代器,所谓的迭代器协议能够简单概括为:
__iter__()
方法,返回一个迭代器迭代器和可迭代对象的区别是:迭代器能够使用next()方法不断调用并返回下一个值,除了调用可迭代对象的__iter__
方法来将可迭代对象转换为迭代器之外,还能够使用iter()方法。
举个例子来验证以上说法:
In [1]: iter_data = iter([1, 2, 3]) In [2]: print(next(iter_data)) 1 In [3]: print(next(iter_data)) 2 In [4]: print(next(iter_data)) 3 In [5]: print(next(iter_data)) --------------------------------------------------------------------------- StopIteration Traceback (most recent call last) <ipython-input-16-425d66e859b8> in <module> ----> 1 print(next(iter_data))
为何要用迭代器?
很重要的一点是,Python把迭代器内建在语言之中的,咱们在遍历一个容器对象时并不须要去实现具体的遍历操做。
迭代器时一个惰性序列,仅仅在迭代至当前元素时才计算该元素的值,在此以前能够不存在,在此以后能够随时销毁,也就是说,在迭代过程当中不是将全部元素一次性加载,这样便不须要考虑内存的问题。经过定义迭代器协议,咱们能够随时实现一个迭代器。
何时用迭代器?
具体在什么场景下能够使用迭代器:
举个最简单的例子:
class Fib(object): def __init__(self): self._a = 0 self._b = 1 def __iter__(self): return self def __next__(self): self._a, self._b = self._b, self._a + self._b return self._a if __name__ == '__main__': for index, item in enumerate(Fib()): print(item) if index >= 9: break
什么是生成器?
生成器,顾名思义,就是按照必定的模式生成一个序列,是一种高级的迭代器,Python中有一个专门的关键字(yield)来实现生成器。
若是一个函数,使用了yield语句,那么它就是一个生成器函数,当调用生成器函数函数时,它返回一个迭代器,不过这个迭代器时一个生成器对象。
举个例子:
from itertools import islice def fib(): a, b = 1, 1 while True: yield a a, b = b, a + b if __name__ == '__main__': fib_data = fib() print(list(islice(fib_data, 0, 10)))
能够看到,使用生成器后,代码简洁了不少!在上述代码中添加:
print(type(fib_data)) print(dir(fib_data))
能够看到函数返回的是一个generator对象,且对象实现了迭代器协议。
可是,使用生成器必需要注意的一点是:生成器只能遍历一次。
何时用生成器呢?
生成器能够使用更少的中间变量来写流式代码, 相比于其它容器对象占用的内存和CPU资源更少一些。当须要一个将返回一个序列或在循环中执行的函数时,就能够使用生成器,由于当这些元素被传递到另外一个函数中进行后续处理时,一次返回一个元素能够有效的提高总体性能,最重要的是,比迭代器简洁!
除此之外,生成器还有两个很棒的用处:
什么是生成器表达式?
列表推导式,你们应该都用到,可是因为内存的限制,列表的容量是有限的,若是要建立一个有几百万个元素的列表,会占用不少的储存空间,当咱们只须要访问几个元素时,其它元素占用的空间就白白浪费了。
这种时候你能够用生成器表达式啊,生成式表达式是一种实现生成器的便捷方式,将列表推导式的中括号替换为圆括号,生成器表达式是一种边循环边计算,使得列表的元素能够在循环过程当中一个个的推算出来,不须要建立完整的列表,从而节省了大量的空间。
In [1]: a = (item for item in range(10)) In [2]: type(a) Out[2]: generator In [3]: next(a) Out[3]: 0 In [4]: next(a) Out[4]: 1
以上。
代码可参考:my github