python--迭代器和生成器详解
迭代器和生成器
在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即可。