迭代器就是一个有 __next__() 方法的对象。当须要下一个数据时,调用它的 __next__() 方法就能够得到。在Python2中,这个方法被命名为 next() 。但在Python3中新增长了内建的 next() 函数,就像用来调用 __iter__() 的 iter() 函数同样,next() 也是用来调用 __next__() 的。 python
就如上面所说,对迭代器来说,有一个__next__()就够了。在你使用for 和 in 语句时,若是可行,程序就会自动调用即将被处理的对象的迭代器对象,而后使用它的__next__()方法,直到监测到一个StopIteration异常。以比较经常使用的 range() 来举例: shell
>>> type(range(10)) <class 'range'> >>> next(range(10)) Traceback (most recent call last): File "<pyshell#163>", line 1, in <module> next(range(10)) TypeError: 'range' object is not an iterator >>> type(iter(range(10))) <class 'range_iterator'> >>> next(iter(range(10))) 0
咱们能够看到,range(10) 自己做为一个 <class 'range'> 对象是不可迭代的,可是 iter(range(10)) 或者说 range(10).__iter__() 能够,它的类(型)是<class 'range_iterator'>,它有 __next__() 方法。那么如何使用迭代器看起来就变得简单了——咱们只要在类里面定义一个 __iter__() 函数,用它来返回一个带 __next__() 方法的对象就够了。其实按照官方文档的说法,迭代器对象里也应该有一个 __iter__() 方法,这个方法只须要一个语句 return self ,这是为了保证在使用 for 语句时,容器对象和迭代器对象均可以被正确调用,就像下面这样: 函数
>>>for x in container:pass >>>for x in iter(container):pass
__iter__() 的设计很是简单,由于咱们大能够把 self 返回,而后在类里面定义 __next__() 。这样定义的一个类,其自身既是容器,又是迭代器(真棒)。那么接下来的主要问题就是设计 __next__() 了。 设计
咱们使用斐波那契数列来举例: code
class Feb: def __init__(self): self.pre = 1 self.num = 0 def __iter__(self): return self def __next__(self): if self.num < 10: self.pre,self.num = self.num,self.pre+self.num return self.num else:raise StopIteration
试运行以下: 对象
>>> feb = Feb() >>> for i in feb: print(i) 1 1 2 3 5 8 13 >>>
嗯,还算正常。for 循环正确迭代了 Feb 实例,正确处理了 StopIteration 异常。只是有一个小Bug,我原本没想让它输出13来着… 文档
咱们能够看到这个 __next__() 实现的不很好,它看起来很麻烦。那么如何“优雅”地实现迭代器呢?固然就是用“生成器”啦! generator
生成器是一个特定的“函数”,当调用时它返回一个生成器对象(因此你不能在定义生成器的时候 return 任何值,但能够使用单独的一个 return ,它表明进度结束)。生成器容许你返回一个值,而后暂停代码的执行,稍后恢复,能够这样重复 n 次。实现这一“优雅”效果的关键字就是 yield it
仍是斐波那契数列: io
def feb(): pre = num = 1 yield pre yield num while True: pre, num = num, pre+num if num<10: yield num else:return
输出以下:嗯,此次没有10以上。
>>> a = feb() >>> for i in a: print(i) 1 1 2 3 5 8 >>>
值得一提的是,这种协同程序是能够在 yield 值出来的时候顺便回传一个参数(或异常)的,它定义时的语法是:foo = (yield bar) 。当程序把 bar 返回出来的时候,程序挂起。这时能够经过使用 generator.send() 方法来给实例里的foo赋值。若是你不想再迭代了,还能够使用 generator.close() 方法来关闭生成器。
下面是一个从可由用户定义的整数 n 开始不断返回 n+1 的生成器:
def plus1(n=0): n = n while True: var = (yield n) if var: n = var else:n += 1
>>> a = plus1()
>>> for i in a: if i>3:break else:print(i) 0 1 2 3 >>> a.send(100) 100 >>> for i in a: if i > 103:break else:print(i) 101 102 103 >>> a.close() >>> next(a) Traceback (most recent call last): File "<pyshell#230>", line 1, in <module> next(a) StopIteration >>>