当前位置 主页 > 服务器问题 > win服务器问题汇总 >

    Python generator生成器和yield表达式详解

    栏目:win服务器问题汇总 时间:2019-12-05 21:22

    前言

    Python生成器(generator)并不是一个晦涩难懂的概念。相比于MetaClass和Closure等概念,其较为容易理解和掌握。但相对于程序结构:顺序、循环和分支而言其又不是特别的直观。无论学习任何的东西,概念都是非常重要的。正确树立并掌握一些基础的概念是灵活和合理运用的前提,本文将以一种通俗易懂的方式介绍一下generator和yield表达式。

    1. Iterator与Iterable

    首先明白两点:

    Iterator(迭代器)是可迭代对象; 可迭代对象并不一定是Iterator;

    比较常见的数据类型list、tuple、dict等都是可迭代的,属于collections.Iterable类型;

    迭代器不仅可迭代还可以被内置函数next调用,属于collections.Iterator类型;

    迭代器是特殊的可迭代对象,是可迭代对象的一个子集。

    将要介绍的gererator(生成器)是types.GeneratorType类型,也是collections.Iterator类型。

    也就是说生成器是迭代器,可被next调用,也可迭代。

    三者的包含关系:(可迭代(迭代器(生成器)))

    迭代器:可用next()函数访问的对象; 生成器:生成器表达式和生成器函数;

    2. Python生成器

    python有两种类型的生成器:生成器表达式和生成器函数。

    由于生成器可迭代并且是iterator,因此可以通过for和next进行遍历。

    2.1 生成器表达式

    把列表生成式的[]改成()便得到生成器表达式。

    >>> gen = (i + i for i in xrange(10))
    >>> gen
    <generator object <genexpr> at 0x0000000003A2DAB0>
    >>> type(gen)
    <type 'generator'>
    >>> isinstance(gen, types.GeneratorType) and isinstance(gen, collections.Iterator) and isinstance(gen, collections.Iterable)
    True
    >>>

    2.2 生成器函数

    python函数定义中有关键字yield,该函数便是一个生成器函数,函数调用返回的是一个generator.

    def yield_func():
      for i in xrange(3):
        yield i
    gen_func = yield_func()
    for yield_val in gen_func:
      print yield_val

    生成器函数每次执行到yield便会返回,但与普通函数不同的是yield返回时会保留当前函数的执行状态,再次被调用时可以从中断的地方继续执行。

    2.3 next与send

    通过for和next可以遍历生成器,而send则可以用于向生成器函数发送消息。

    def yield_func():
      for i in xrange(1, 3):
        x = yield i
        print 'yield_func',x
    gen_func = yield_func()
    print 'iter result: %d' % next(gen_func)
    print 'iter result: %d' % gen_func.send(100)

    结果:

    iter result: 1
    yield_func 100
    iter result: 2

    简单分析一下执行过程:

    line_no 5 调用生成器函数yield_func得到函数生成器gen_func; line_no 6 使用next调用gen_func,此时才真正的开始执行yield_func定义的代码; line_no 3 执行到yield i,函数yield_func暂停执行并返回当前i的值1. line_no 6 next(gen_func)得到函数yield_func执行到yield i返回的值1,输出结果iter result: 1; line_no 7 执行gen_func.send(100); line_no 3 函数yield_func继续执行,并将调用者send的值100赋值给x; line_no 4 输出调用者send接收到的值; line_no 3 执行到yield i,函数yield_func暂停执行并返回当前i的值2. line_no 7 执行gen_func.send(100)得到函数yield_func运行到yield i返回的值2,输出结果iter result: 2;