大家都用过for循环,但是大家知道Python for循环背后的工作原理吗?本文就带大家详细探究Python for循环是如何工作的以及iterable和iterator的概念。


首先我们来看一下,如果不用for循环,是否还有其它的遍历方式。一种方法就是用类似C语言中通过索引进行访问:

colors = ["red", "green", "blue", "purple"]i = 0while i < len(colors):print(colors[i])i += 1

对于list这么做没问题,但是如果遍历对象是set就行不通了:

>>> colors = {"red", "green", "blue", "purple"}>>> i = 0>>> while i < len(colors):... print(colors[i])... i += 1...Traceback (most recent call last):File "", line 2, inTypeError: 'set' object does not support indexing

问题就出在,这种方法只能用在序列对象上,也就是能通过索引进行访问的数据结构,比如list,string,tuple等。但是对于dictionary,set这些非序列结构就无能为力了。

Iterable

在Python的世界中,一个iterable就是可以用过for循环进行遍历的对象。Iterable不一定具有索引,也不一定有长度,甚至不一定是有限长度的。

下面就是一个无限长的iterable的例子,它包含了所有5的倍数:

from itertools import countmultiples_of_five = count(step=5)

当我们用for对其进行遍历时,可以这样做:

for n in multiples_of_five:if n > 100:breakprint(n)

如果我们把break去掉的话,这个循环就会永远进行下去。

Iterable和Iterator

我们知道iterable是什么了,但是它在Python中是如何工作的呢?

Python中有一个内建函数iter,任何iterable传进去都会获得其对应的iterator。

>>> iter(['some', 'list'])>>> iter({'some', 'set'})>>> iter('some string')

然而iterator究竟是什么呢?简单地说,iterator只完成一个功能,那就是在遍历中返回下一个元素。

我们可以从任意iterable获取一个iterator:

>>> iterator = iter('hi')

然后利用next内建函数去获取下一个元素:

>>> next(iterator)'h'>>> next(iterator)'i'>>> next(iterator)Traceback (most recent call last):File "", line 1, inStopIteration

如果遍历结束了,即没有下一个元素了,next函数会抛出一个StopIteration异常。

Iterator同时也是iterable

将iterable传入iter函数可以得到一个iterator。同时,如果将一个iterator传入iter函数,得到的结果就是这个iterator本身。这意味着,iterator本身也是iterable。

>>> iterator = iter('hi')>>> iterator2 = iter(iterator)>>> iterator is iterator2True

Iterator协议

在Python中,所有的iterator都遵循一套相同的协议:

1 一个iterable传入iter函数后可以得到对应的iterator。

2 iterator可以作为参数传入next函数,返回下一个元素或者抛出StopIteration异常。

3 当作iterator为参数传入iter函数时返回自身。

上述规则反过来也是成立的:

1 任何对象如果可以作为参数传入iter并且没有异常,则该对象是iterable。

2 任何对象如果可以作为参数传入next并且没有异常(StopIteration除外),则该对象是iterator。

3 任何传入iter并得到其自身的对象是iterator。

对iterator进行遍历

下面这段代码对一个iterable进行遍历,并打印其中的每一个元素:

def print_each(iterable):iterator = iter(iterable)while True:try:item = next(iterator)except StopIteration:break # Iterator exhausted: stop the loopelse:print(item)

任意iterable都可以作为该函数的参数:

>>> print_each({1, 2, 3})123

事实上,上面这段代码和下面使用for的代码是等价的:

def print_each(iterable):for item in iterable:print(item)

在for循环中Python会自动实现我们手工实现的功能:调用iter去获取下一个元素,直到遇到StopIteration异常。

这就是隐藏在Python for循环背后的秘密,小伙伴们,你们get到了吗?