【python小技巧4】for循环原理与迭代器的实现

对于新手,for 循环无非 for i in range(10): for i in lst: 等,那你知道 for 循环真正的原理吗?
目录
你需要知道的...
for 循环的工作原理(2种情况)
如何写一个可迭代对象和迭代器

你需要知道的...
for 循环的语法永远是 for xxx in xxx,for 循环又称遍历循环,本质上一切 for 循环,都是在遍历一个对象,无论是 range 还是 list。
内置函数 与 类的魔术方法 的关系
python 里有很多“魔术方法”,它们都有一个共同点:以双下划线开头,以双下划线结尾,如 __str__,__int__,__iter__,__next__ 等。(这也是为什么不推荐新手用 __xxx__ 这种当变量名的原因)
其中有很多魔术方法可被对应的内置函数调用,如 str(), int(), iter(), next() 等。
本文还涉及了一个魔术方法 __getitem__
obj.__getitem__(item) <==> obj[item],就是列表的取元素操作
可迭代对象 与 迭代器
可迭代对象指实现 __iter__ 方法,一般保存了数据,如 list 等。
迭代器指实现了 __next__ 方法,一般保存了迭代的状态(如迭代到哪一个),如 list_iterator 等。
严格定义如上,但有一条规定:迭代器应该也是可迭代的。换句话说:如果一个对象实现了 __next__ 方法,那它也应该实现 __iter__ 方法,大部分就是返回自己。
可迭代对象 与 迭代器的概念一定要记住,很重要!!!
如何检测一个对象是否是迭代器或可迭代?使用内置模块 collections(新版建议用collections.abc 模块) 下的 Iterable 和 Iterator 就可以,具体用法:
注意:字符串、列表、元组、字典、集合等是可迭代的,但不是迭代器!

假设有一个循环:
以下将在这个循环的基础上讲解
第一种情况:
如果 obj 是可迭代的,即实现了 __iter__ 方法,尝试调用 iter(obj) 得到 obj 的迭代器,假设迭代器是 iterator。
然后不断调用 next(iterator),返回值就是迭代出来的值,直到遇到 StopIteration,停止循环。
这种情况下上面的代码与下面是等价的:
第二种情况:
如果 obj 没有实现 __iter__ 方法,则康康有没有实现 __getitem__ 方法。
如果有,则从 0 开始,依次递增 1 来调用 __getitem__ 方法,返回值就是迭代出来的值,直到遇到 StopIteration,停止调用。
这种情况下上面的代码与下面是等价的:
如果以上两种方法都行不通,那就不能用 for 循环(会报TypeError: 'xxx' object is not iterable)

下面再来讲一讲如何实现可迭代对象和迭代器
实现可迭代对象和迭代器
我们试着模仿 range 函数写一个生成等差数列的可迭代对象
现在它既不是迭代器,也不可迭代
我们试着让它的迭代器就是它自己(实现 __iter__ 方法):
已经可迭代了,接下来让它成为迭代器
我们可以对它用 for 循环了:
但是,这种写法有问题,慢慢看来:
我们要输出一个 [1, 2, 3, 4, 5] 与它自己的笛卡尔积(不知道啥是笛卡尔积的看这里https://baike.baidu.com/item/%E7%AC%9B%E5%8D%A1%E5%B0%94%E4%B9%98%E7%A7%AF/6323173),即
传统写法是
这很简单。但如果用咱们编的这个 Range,问题就出来了,看输出:
问题就出在:因为有 return self 的存在,两个 for 循环共用了一个迭代器(换而言之,这个 Range 对象只能用一次)!让这个 Range 对象既保存数据又保存状态是不行哒!


所以我们需要这里给出两种解决方案:
(一)自定义专门的迭代器
最本质的问题就是 __iter__ 方法里的 return self,我们再定义一个迭代器:
为了要满足第三条规则,即迭代器应该也是可迭代的,上面也说了,大部分就是返回自己,所以再给 RangeIterator 加上 __iter__ 方法:
这样,Range 对象就可以多次使用了,但 RangeIterator 作为迭代器本来就只能使用一次。
至此,一个迭代器已经实现了。
(二)使用生成器
我们下一章详细讨论生成器,这里不理解没关系(挖坑*1)
我们将 __iter__ 方法变成一个生成器函数:
也能达到效果,且更加简洁,推荐使用这种方法。
至于我们刚才讨论的第二种情况,实际使用中很少使用,我们就不再赘述。
END

这算是给之前的一章填坑了...
参考资料:
https://docs.python.org/3.8/tutorial/classes.html
https://docs.python.org/3.8/library/stdtypes.html
https://baike.baidu.com/item/%E7%AC%9B%E5%8D%A1%E5%B0%94%E4%B9%98%E7%A7%AF/6323173