zoukankan      html  css  js  c++  java
  • python之迭代器和生成器

    一、迭代器(iterator)

    在Python中,for循环可以用于Python中的任何类型,包括列表、元祖等等,实际上,for循环可用于任何“可迭代对象”,这其实就是迭代器

    迭代器是一个实现了迭代器协议的对象,Python中的迭代器协议就是有next方法的对象会前进到下一结果,而在一系列结果的末尾是,则会引发StopIteration。任何这类的对象在Python中都可以用for循环或其他遍历工具迭代,迭代工具内部会在每次迭代时调用next方法,并且捕捉StopIteration异常来确定何时离开。

    使用迭代器一个显而易见的好处就是:每次只从对象中读取一条数据,不会造成内存的过大开销。

    比如要逐行读取一个文件的内容,利用readlines()方法,我们可以这么写:

    1
    2
    for line in open("test.txt").readlines():
    print line

    这样虽然可以工作,但不是最好的方法。因为他实际上是把文件一次加载到内存中,然后逐行打印。当文件很大时,这个方法的内存开销就很大了。

    利用file的迭代器,我们可以这样写:

    1
    2
    for line in open("test.txt"):   #use file iterators
    print line

    这是最简单也是运行速度最快的写法,他并没显式的读取文件,而是利用迭代器每次读取下一行。

    二、生成器(constructor)

    生成器函数在Python中与迭代器协议的概念联系在一起。简而言之,包含yield语句的函数会被特地编译成生成器。当函数被调用时,他们返回一个生成器对象,这个对象支持迭代器接口。函数也许会有个return语句,但它的作用是用来yield产生值的。

    不像一般的函数会生成值后退出,生成器函数在生成值后会自动挂起并暂停他们的执行和状态,他的本地变量将保存状态信息,这些信息在函数恢复时将再度有效

    1
    2
    3
    4
    5
    6
    7
    8
    >>> def g(n):
    ... for i in range(n):
    ... yield i **2
    ...
    >>> for i in g(5):
    ... print i,":",
    ...
    0 : 1 : 4 : 9 : 16 :

    要了解他的运行原理,我们来用next方法看看:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    >>> t = g(5)
    >>> t.next()
    0
    >>> t.next()
    1
    >>> t.next()
    4
    >>> t.next()
    9
    >>> t.next()
    16
    >>> t.next()
    Traceback (most recent call last):
    File "<stdin>", line 1, in <module>
    StopIteration

    在运行完5次next之后,生成器抛出了一个StopIteration异常,迭代终止。
    再来看一个yield的例子,用生成器生成一个Fibonacci数列:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    def fab(max):
    a,b = 0,1
    while a < max:
    yield a
    a, b = b, a+b
     
    >>> for i in fab(20):
    ... print i,",",
    ...
    0 , 1 , 1 , 2 , 3 , 5 , 8 , 13 ,

    看到这里应该就能理解生成器那个很抽象的概念了吧~~

     def read_file(fpath): 
        BLOCK_SIZE = 1024 
        with open(fpath, 'rb') as f: 
            while True: 
                block = f.read(BLOCK_SIZE) 
                if block: 
                    yield block 
                else: 
                    return
    复制代码

    如果直接对文件对象调用 read() 方法,会导致不可预测的内存占用。好的方法是利用固定长度的缓冲区来不断读取文件内容。通过 yield,我们不再需要编写读文件的迭代类,就可以轻松实现文件读取。

  • 相关阅读:
    [2016北京集训试题15]项链-[FFT]
    [agc008E]Next or Nextnext-[dp+思考题]
    [agc011E]Increasing Numbers-[思考题]
    [2016北京集训试题14]股神小D-[LCT]
    [2016北京集训试题6]mushroom-[bitset]
    [2016北京集训试题6]魔法游戏-[博弈论-sg函数]
    [arc081F]Flip and Rectangles-[黑白染色]
    [arc072F]Dam-[单调队列]
    【CF787D】遗产(Legacy)-线段树-优化Dijkstra(内含数据生成器)
    【CF373C】计算袋鼠是愉快的(Counting Kangaroos is Fun)-贪心
  • 原文地址:https://www.cnblogs.com/work115/p/5822528.html
Copyright © 2011-2022 走看看