Python迭代器与生成器详解(3)

先定义一个可迭代对象(包含__iter__方法),然后该对象返回一个迭代器;这样看上去是不是很麻烦?是不是同时带有__iter__和__next__魔术方法的迭代器更好呢!

同时,这里要纠正之前的一个迭代器概念:只要__next__()(python2中实现next())方法的对象都是迭代器;

既然这样,只需要迭代器Iterator接口就够了,为什么还要设计可迭代对象Iterable呢?

这个和迭代器不能重复使用有关,下面同意讲解:

3)总结和一些重要知识点

a) 如何复制迭代器

之前在使用enumerate时,我们说过enumerate对象通过for循环迭代一次后就不能再被迭代:

>>> e = enumerate([1,2,3]) >>> >>> for x,y in e: ... print(x,y) ... 0 1 1 2 2 3 >>> for x,y in e: ... print(x,y) ... >>>

这是因为enumerate是一个迭代器;

迭代器是一次性消耗品,当循环以后就空了。不能再次使用;通过深拷贝可以解决;

>>> import copy >>> >>> e = enumerate([1,2,3]) >>> >>> e_deepcopy = copy.deepcopy(e) >>> >>> for x,y in e: ... print(x,y) ... 0 1 1 2 2 3 >>> for x,y in e_deepcopy: ... print(x,y) ... 0 1 1 2 2 3 >>>

b)为什么不只保留Iterator的接口而还需要设计Iterable呢?

因为迭代器迭代一次以后就空了,那么如果list,dict也是一个迭代器,迭代一次就不能再继续被迭代了,这显然是反人类的;所以通过__iter__每次返回一个独立的迭代器,就可以保证不同的迭代过程不会互相影响。而生成器表达式之类的结果往往是一次性的,不可以重复遍历,所以直接返回一个Iterator就好。让Iterator也实现Iterable的兼容就可以很灵活地选择返回哪一种。

总结说,Iterator实现的__iter__是为了兼容Iterable的接口,从而让Iterator成为Iterable的一种实现。

另外,迭代器是惰性的,只有在需要返回下一个数据时它才会计算。就像一个懒加载的工厂,等到有人需要的时候才给它生成值返回,没调用的时候就处于休眠状态等待下一次调用。所以,Iterator甚至可以表示一个无限大的数据流,例如全体自然数。而使用list是永远不可能存储全体自然数的。

c)通过__getitem__来实现for循环

前面关于可迭代对象的定义是这样的:定义了可以返回一个迭代器的__iter__方法,或者定义了可以支持下标索引的__getitem__方法,那么它就是一个可迭代对象。

但是如果对象没有__iter__,但是实现了__getitem__,会改用下标迭代的方式。

class NoIterable(object): def __init__(self, data): self.data = data def __getitem__(self, item): return self.data[item] no_iter = NoIterable('abcde') for item in no_iter: print(item)

 

当for发现没有__iter__但是有__getitem__的时候,会从0开始依次读取相应的下标,直到发生IndexError为止,这是一种旧的迭代方法。iter方法也会处理这种情况,在不存在__iter__的时候,返回一个下标迭代的iterator对象来代替。

d)一张图总结迭代器

Python迭代器与生成器详解

e)使用迭代器来实现一个斐波那契数列

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/e6a0c31b6f1b387c60317b92283f0191.html