迭代器和生成器
在Python这门语言中,生成器毫无疑问是最有用的特性之一。生成器同时也满足iterator与iterable。
迭代器
这里有两个概念:Iterable(可迭代)与Iterator。
实现了__next__方法,就叫做Iterator,注意__next__要有退出条件。
实现了__iter__方法就是Iterable,且这个方法返回的是一个Iterator。
所以一个类中我可以不用全部实现__iter__和__next__,只用实现一个,于是我试了一下像这样:
class Iterable(object): def __init__(self,MaxIter): self.MaxIter = MaxIter self.Iterator = Iterator(self.MaxIter) def __iter__(self): return self.Iterator class Iterator(object): def __init__(self,MaxIter): self.a, self.b = 0,1 self.iterNum = 0 self.MaxIter = MaxIter def __next__(self): self.iterNum += 1 self.a, self.b = self.b, self.a + self.b if self.iterNum > self.MaxIter: # 退出条件 raise StopIteration() # 要用raise StopIteration()来推出 return self.a for i in Iterable(10): print(i)
输出:
1 1 2 3 5 8 13 21 34 55
Iterable只实现了iter,Iterator只实现了next。for里面需要就收的是一个满足Iterable条件(实现__iter__)的对象,所以我们传入的是Iterable(10)。
这里,Iterable的__iter__只执行了一次,返回了一个Iterator,之后for里面每次就调用Iterator里的__next__得到一个返回值。但通常情况下是一个类既实现了__iter__又实现了__next__,在__iter__里面返回自身也就是return self
,这也是官方推荐的做法。
官方定义,可知iterator需要同时实现__iter__和__next__。
Classes can define how they are iterated over by defining an
__iter__()
method; this should take no additional arguments and return a valid iterator object. A class that wants to be an iterator should implement two methods: anext()
method that behaves as described above, and an__iter__()
method that returns self.
The two methods correspond to two distinct protocols:
- An object can be iterated over with for if it implements iter() or getitem().
- An object can function as an iterator if it implements next().
我再来看看list实现了那些函数时:
a = [1,2,3] print(dir(a)) Output: ['__add__', '__class__', '__contains__', '__delattr__', '__delitem__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__getitem__', '__gt__', '__hash__', '__iadd__', '__imul__', '__init__', '__init_subclass__', '__iter__', '__le__', '__len__', '__lt__', '__mul__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__reversed__', '__rmul__', '__setattr__', '__setitem__', '__sizeof__', '__str__', '__subclasshook__', 'append', 'clear', 'copy', 'count', 'extend', 'index', 'insert', 'pop', 'remove', 'reverse', 'sort']
没有发现__next__,我猜这里的__getitem__实现了__next__类似的功能,只不过,__getitem__是可以根据索引来获取值。
生成器
理解了迭代器后,生成器就好说了。
共有两种方式提供生成器:
- 生成器函数,只是return 变成了yeild,每次返回一个结果,而不是全部结果。
- 生成器表达式,例如
(i for i in range(10))
圆括号的列表推导返回的就是生成器。
生成器函数
# A generator function that yields 1 for first time, # 2 second time and 3 third time def simpleGeneratorFun(): yield 1 yield 2 yield 3 # Driver code to check above generator function for value in simpleGeneratorFun(): print(value) Output : 1 2 3
由生成器函数返回的对象是生成器对象。例如
# A simple generator for Fibonacci Numbers def fib(limit): # Initialize first two Fibonacci Numbers a, b = 0, 1 # One by one yield next Fibonacci Number while a < limit: yield a a, b = b, a + b # Create a generator object x = fib(5)
这里可以通过for循环调用这个计算斐波拉契数列的生成器对象。
print("\nUsing for in loop") for i in fib(5): print(i) OutPut: Using for in loop 0 1 1 2 3
既然可以通过for迭代访问,那么生成器肯定实现了__iter__,通过dir(x)
发现:
['__class__', '__del__', '__delattr__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__iter__', '__le__', '__lt__', '__name__', '__ne__', '__new__', '__next__', '__qualname__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', 'close', 'gi_code', 'gi_frame', 'gi_running', 'gi_yieldfrom', 'send', 'throw']
里面果然有__iter__和__next__并且__iter__返回的就是它本身,也就是一个生成器。
也就是说生成器满足可迭代条件,由于实现了__next__它也是迭代器。
应用:
sum([i for i in xrange(10000000000)]) sum(i for i in xrange(10000000000))
如果不用生成器,对于前一个表达式,电脑会卡死。而后一个生成器,由于每次返回一个值来求和,所以几乎不占什么内存。
注意
由于每次返回一个值,没有保存所有的值,生成器只能遍历一遍,也即遍历到退出条件的时候,遍历文件也即遍历到文件尾。再次遍历的话,就什么都不会返回,除非重新创建生成器。
总结
实现了__iter__
方法的,也就满足了可迭代条件,并且__iter__
需返回一个迭代器(iterator)也就是实现了__next__
的迭代器对象。一般我们需要同时实现__iter__
与__next__
,__iter__
返回自身。而生成器同时实现了__iter__
与__next__
所以它同时满足iterator与iterable,只需要我们将函数中的return换成yeild即可。
来源:https://www.cnblogs.com/ivan-blog/p/12425894.html