python--迭代器和生成器详解

痞子三分冷 提交于 2020-03-06 14:13:22

迭代器和生成器

在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: a next() method that behaves as described above, and an __iter__() method that returns self.
The two methods correspond to two distinct protocols:

  1. An object can be iterated over with for if it implements iter() or getitem().
  2. 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即可。

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!