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

    迭代器

    Iterator是迭代器的意思,它的作用是一次产生一个数据项,直到没有为止。这样在 for 循环中就可以对它进行循环处理了。那么它与一般的序列类型(list, tuple等)有什么区别呢?它一次只返回一个数据项,占用更少的内存。但它需要记住当前的状态,以便返回下一数据项。它是一个有着next()方法的对象。而序列类型则保存了所有的数据项,它们的访问是通过索引进行的。

    迭代器是一个实现了迭代器协议的对象,Python中的迭代器协议就是有next方法的对象会前进到下一结果,而在一系列结果的末尾是,则会引发StopIteration。

    在for循环中,Python将自动调用工厂函数iter()获得迭代器,自动调用next()获取元素,还完成了检查StopIteration异常的工作。

    常用的几个内建数据结构tuple、list、set、dict都支持迭代器,字符串也可以使用迭代操作。

    你也可以自己实现一个迭代器,如上所述,只需要在类的__iter__方法中返回一个对象,这个对象拥有一个next()方法,这个方法能在恰当的时候抛出StopIteration异常即可。但是需要自己实现迭代器的时候不多,即使需要,使用生成器会更轻松。

    复制代码
    #!/usr/bin/env python
    # coding=utf-8
    
    class test:
        def __init__(self, input_list):
            self.list = input_list
            self.i = 0
    
        def __iter__(self):
            return self
        
        def next(self):
            if self.i == len(self.list):
                self.i = 0
                raise StopIteration
            self.i += 1
            return  self.list[self.i - 1]
    复制代码

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

    例如:

    复制代码
    /* 把文件一次加载到内存中,然后逐行打印。当文件很大时,这个方法的内存开销就很大了 */
    for line in open("test.txt").readlines():
        print line
    
    /* 这是最简单也是运行速度最快的写法,他并没显式的读取文件,而是利用迭代器每次读取下一行 */
    for line in open("test.txt"):   #use file iterators
        print line
    复制代码

    生成器

    生成器是创建迭代器的一种简便的方法。生成器是一个特殊的函数。我们可以从静态和动态两个角度理解生成器函数。

    首先,从静态的角度,生成器函数在代码中表现为:

    1. 含有yield语句(无论yield是否可能会被执行)
    2. 无return或者仅有无值return(一旦函数里存在yield语句,有值return会视为语法错误)

    其次,从动态的角度,生成器函数在运行过程中:

    1. 当生成器函数被调用的时候,生成器函数不执行内部的任何代码,直接立即返回一个迭代器。
    2. 当所返回的迭代器第一次调用next的时候,生成器函数从头开始执行,如果遇到了执行yield x,next立即返回yield值x。
    3. 当所返回的迭代器继续调用next的时候,生成器函数从上次yield语句的下一句开始执行,直到遇到下一次执行yield
    4. 任何时候遇到函数结尾,或者return语句,抛出StopIteration异常

    特别的,生成器返回的迭代器,其__iter__返回其自身。

    生成器的编写方法和函数定义类似,只是在return的地方改为yield。

    生成器中可以有多个yield。当生成器遇到一个yield时,会暂停运行生成器,返回yield后面的值。当再次调用生成器的时候,会从刚才暂停的地方继续运行,直到下一个yield。

    生成器自身又构成一个迭代器,每次迭代时使用一个yield返回的值。

    需要注意的是,生成器中不需要return语句,不需要指定返回值,在生成器中已经存在默认的返回语句

    生成器表达式

    (i for i in range(5))
    // 返回迭代器
    <generator object <genexpr> at 0x7ff3e8f0d960>

    列表解析,返回list

    [i for i in range(5)]
    // 返回list
    [0, 1, 2, 3, 4]

     在这里存在一个问题,那就是range(5)会返回一个长度为5的数据,如果是range(1000)那么就会占用一个1000大小的数组空间;如果我们采用`生成器`,在需要的时候产生一个数字,那么空间的占用情况就会降低,这里我们可以使用xrange()函数来实现。

    复制代码
    '''
    xrange
        函数说明:用法与range完全相同,所不同的是生成的不是一个数组,而是一个生成器。
    xrange示例:
    ''' 
    >>> xrange(5)
    xrange(5)
    >>> list(xrange(5))
    [0, 1, 2, 3, 4]
    >>> xrange(1,5)
    xrange(1, 5)
    >>> list(xrange(1,5))
    [1, 2, 3, 4]
    >>> xrange(0,6,2)
    xrange(0, 6, 2)
    >>> list(xrange(0,6,2))
    [0, 2, 4]
    复制代码

    所以xrange做循环的性能比range好,尤其是返回很大的时候,尽量用xrange吧,除非你是要返回一个列表。

    所以,Python 2.X 的 range和xrange有何区别?答案是,range的返回值就是一个list,在你调用range的时候,Python会产生所有的元素。而xrange是一个特别设计的可迭代对象,它在建立的时候仅仅保存终止值。你可比较以下两种写法的实际运行结果:

    for v in range(1000000000000): #possible Memory Error
        if v == 2:
            break
        
    for v in xrange(1000000000000): #fine
        if v == 2:
            break
    

      

    在Python 3.X 中,不再有内建的xrange,其range等效于Python 2.X 的xrange

  • 相关阅读:
    .Net Frameworks versions
    noteJavascript::string2Date for new Date(str)
    noteMS questions
    note删除Visual Studio recent Projects list
    noteshow hidden files
    sql trick
    拯救MySql 签名
    数据结构——栈和队列
    the summary of loop structure in C
    the summury of array in C.
  • 原文地址:https://www.cnblogs.com/2014-02-17/p/6959409.html
Copyright © 2011-2022 走看看