zoukankan      html  css  js  c++  java
  • 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: 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("
    Using 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即可。

  • 相关阅读:
    查看数据表的信息
    HR面试必问:你什么时候能到岗?聪明人都这样回答
    42种常见的HTTP响应代码
    Ubuntu环境下微信界面中间显示黑框和字体显示为白框等问题
    C# 开发电子印章制作工具 -- 附下载程序
    ofd文件拆分合并思路探索 -- 附下载程序
    利用防火墙出站规则解决Adobe Rader XI闪退问题
    记一次 Aximp.exe工具的使用
    MacOS Monterey 一键重置系统
    macbook m1 键盘进灰导致个别按键不灵敏解决方法
  • 原文地址:https://www.cnblogs.com/ivan-blog/p/12425894.html
Copyright © 2011-2022 走看看