美文网首页
Python 迭代器、生成器与yield

Python 迭代器、生成器与yield

作者: filterc | 来源:发表于2018-10-26 10:27 被阅读24次

    一张图概览:

    relations

    container:容器

    容器是一种把多个元素组织在一起的数据结构,容器中的元素可以逐个地迭代获取,可以用in, not in关键字判断元素是否包含在容器中。

    比如常用的list、set、dict等。

    iterable:可迭代对象

    很多容器都是可迭代对象,此外还有,比如处于打开状态的files,sockets等等,也是可迭代的对象。

    可迭代对象是一个通用的说法,但凡是可以返回一个迭代器的对象都可称之为可迭代对象,也就是调用iter()可以返回一个iterator。

    iterator:迭代器

    迭代器是一个带状态的对象,任何实现了iternext()(python2中实现next())方法的对象都是迭代器。
    iter返回迭代器自身,next返回容器中的下一个值。
    如果容器中没有更多元素了,则抛出StopIteration异常。

    iterable-vs-iterator.png
    x = [1, 2, 3]
    for elem in x:
        print elem
    
    # x是一个list,容器,同时也是可迭代对象
    # 这段代码,在运行时,会调用iter(x)获得一个迭代器
    # 然后对迭代器调用next(...)
    
    >>> x = [1, 2, 3]
    >>> y = iter(x)
    >>> z = iter(x)
    >>> next(y)
    1
    >>> next(y)
    2
    >>> next(z)
    1
    >>> type(x)
    <class 'list'>
    >>> type(y)
    <class 'list_iterator'>
    

    y和z是两个独立的迭代器,状态(也就是调用next返回的值)是分开存储的。

    from itertools import slice
    class Fib:
        def __init__(self):
            self.prev = 0
            self.curr = 1
     
        def __iter__(self):
            return self
     
        def __next__(self):
            value = self.curr
            self.curr += self.prev
            self.prev = value
            return value
     
    >>> f = Fib()
    >>> list(islice(f, 0, 10))
    [1, 1, 2, 3, 5, 8, 13, 21, 34, 55]
    

    Fib是一个迭代器。实例变量prev和curr用户维护迭代器内部的状态。每次调用next()方法的时候做两件事:

    • 为下一次调用next()方法修改状态
    • 为当前这次调用生成返回结果

    迭代器就像一个懒加载的工厂,等到有人需要的时候才给它生成值返回,没调用的时候就处于休眠状态等待下一次调用。

    generator:生成器

    生成器其实是一种特殊的迭代器,不过这种迭代器更加优雅。它不需要再像上面的类一样写iter()和next()方法了,只需要一个yiled关键字。

    def fib():
        prev, curr = 0, 1
        while True:
            yield cure
            prev, curr = curr, curr + prev
     
    >>> f = fib()
    >>> list(islice(f, 0, 10))
    [1, 1, 2, 3, 5, 8, 13, 21, 34, 55]
    

    fib就是一个普通的python函数,特殊的地方在于没有return关键字,函数的返回值是一个生成器对象。
    当执行f=fib()返回的是一个生成器对象,此时函数体中的代码并不会执行,只有显示或隐示地调用next的时候才会真正执行里面的代码。

    # 可用生成器重构的典型结构
    
    def something():
        result = []
        for ... in ...:
            result.append(x)
        return result
    
    # 可替换为
    
    def iter_something():
        for ... in ...:
            yield x
    

    generator expression 生成器表达式

    生成器不仅可以用函数实现,也可以用表达式来实现。

    # 通过列表来创建生成器
    [i for i in xrange(10)]
    
    # 等同于
    # 通过`yield`来创建生成器
    def func():
       for i in xrange(10);
            yield i
    

    yield

    带有 yield 的函数不再是一个普通函数,而是一个生成器generator。
    yield 是一个类似 return 的关键字,迭代一次遇到yield时就返回yield后面的值,并且记住当前代码的位置,并在下一次迭代时,从yield后面的代码开始执行。

    def simpleYield(n):  
        for i in range(n):  
            yield call(i)
            print "simpleYield: %d" %i      
        print("end.")  
    
    def call(i):
      print 'call: %d' %i
      return i * 2
      
    def testYield():
      for i in simpleYield(3):  
        print 'testYeild: %d' %i 
        
    ## 运行结果
    call: 0
    testYeild: 0
    simpleYield: 0
    call: 1
    testYeild: 2
    simpleYield: 1
    call: 2
    testYeild: 4
    simpleYield: 2
    end.
    

    可以看到:

    • 在第一次运行的时候,遇到yield,调用了call,然后返回了
    • 第二次运行的时候,从yield后面的 print "simpleYield: %d" %i 开始运行

    另外,带有yield的函数不仅仅只用于for循环中,而且可用于某个函数的参数,只要这个函数的参数允许迭代参数。比如array.extend函数,它的原型是array.extend(iterable)。

    常用生成器工具

    生成器常用来生成序列,而又不用将所有列表都保存在内存里。

    # 生成无限序列:
    >>> from itertools import count
    >>> counter = count(start=13)
    >>> next(counter)
    13
    >>> next(counter)
    14
    
    # 从一个有限序列中生成无限序列:
    >>> from itertools import cycle
    >>> colors = cycle(['red', 'white', 'blue'])
    >>> next(colors)
    'red'
    >>> next(colors)
    'white'
    >>> next(colors)
    'blue'
    >>> next(colors)
    'red'
    

    参考:

    相关文章

      网友评论

          本文标题:Python 迭代器、生成器与yield

          本文链接:https://www.haomeiwen.com/subject/nokktqtx.html