python 生成器yield

来源:互联网 发布:北京飞秋网络 编辑:程序博客网 时间:2024/05/17 23:42

生成器概念

生成器是一次生成一个值的特殊类型函数。可以将其视为可恢复函数。调用该函数将返回一个可用于生成连续 x 值的生成器。
包含yield关键字的函数可以理解是一个用函数名表示的另类list对象,通过迭代的方式(例如:for)依次获取结果。

生成器的特点

  1. 记录执行函数内的所有变量状态(数据)和代码执行位置。
  2. 立即返回yield标记的变量或值,再次调用函数时,代码从上次返回yield之后继续执行。

认识生成器

下来让我们一起来看看如何使用yield,先通过一个简单的例子认识生成器。

def addlist(alist):    for i in alist:        # yield与return功能类似,返回本次迭代结果        yield i + 1alist = [1, 2, 3, 4]for x in addlist(alist):    print(x)

执行结果:

2345

根据addlist()函数实现可以推断出addlist(alist)相当于一个值为[2, 3, 4, 5]的list,不过迭代调用时以函数表达式的形式出现在for语句中。
使用”list变量”(实际为函数变量,此处可以理解为list)替代addlist(alist)作为for的参数,执行结果相同,代码如下:

add_one_list = addlist(alist)for x in add_one_list:    print(x)

生成器执行过程

到底yield是如何执行的呢?生成器(函数)的执行逻辑到底是什么样子?
让我们一起来写个demo进行分析。

next()函数
用于移动迭代器指向下一个数据,直到抛出StopIteration,代表迭代终止。

定义函数yield_run_logic(),对一个已经初始化变量a,多次调用,同在yield前后输出日志,用于分析执行逻辑。然后再一个while(1)的死循环中使用next()方法遍历生成器,直到抛出停止迭代异常跳出循环(StopIteration)。

def yield_run_logic():    a = 1    for i in range(3):        print('begin yield')        yield a        print('end yield')        a += i# 获取生成器对象yield_obj = yield_run_logic()# 此时只是获取对象,尚未执行yield_run_logic()的内容,直到第一调用next()\while 1:    try:        ret = next(yield_obj)        # 第一次调用next()方法,开始执行yield_run_logic(),直到遇到yield关键字,返回当前结果,        # yield_run_logic()保留当前执行状态和代码执行位置,当下次执行next()时,        # yield_run_logic()从上次保留的继续执行,直到遇到yield关键字。        # yield_run_logic()无迭代数据时,抛出StopIteration异常,表示迭代结束。        print(ret)    except StopIteration:        print('end test')        break

执行结果:

# 第1次执行输出 a = 1, i = 0begin yield1# 第2次执行输出 a = 1, i = 1end yieldbegin yield2# 第3次执行输出 a = 2, i = 2end yieldbegin yield4# 第4次执行end yield # yield_run_logic()中 i 超过range(3)的范围,结束迭代,          # 由于使用next()而并非使用for来迭代生成器,所以迭代结束异常需要自己处理。end test

根据输出结果可以看出,在第一次执行next()时,生成器yield_run_logic()开始执行,打印’begin yield’,并在yield a处返回结果,之后再执行next()都是从yield a之后开始执行(打印’end yield’),直到循环中再次遇到yield a时返回。

总结 yield的执行逻辑。

第一次获取迭代结果,函数的第一行代码执行到yield关键字返回,第[2,n]次迭代结果,函数执行从yield关键字下一行代码到再次碰到yield关键字返回。

yield的应用场景

凡是本次处理使用上次处理结果作为基础数据的逻辑实现,都可以考虑使用生成器。

练习1:打印杨辉三角

          1        1   1      1   2   1    1   3   3   1  1   4   6   4   11   5   10  10  5   1

特征分析:从第三行开始每一行的非边界数字等于上一行相邻两数字之和,边界数字全部为1。

def pascals_triangle(line_count):    up_line = []    line = []    # i取值范围[0, line_count-1]    for i in range(line_count):        # 扩展当前行列表长度,初始化数据为1,并且保证边界数据为1        line.append(1)        # 1,2行全部是1,3行开始体现特征,需要进行特征处理        if i > 1:            # 当前行[1, n-1]的值等于上一行[0, n-2]与[1, n-1]的和            for j in range(1, len(up_line)):                line[j] = up_line[j-1] + up_line[j]        # 返回当前行        yield line        # 更新上一行记录结果,保存当前行        up_line = line[:]lines = 6fn = pascals_triangle(lines)for line in fn:    print(line)

本例未将计算结果进行格式化输出,函数执行结果如下:

[1][1, 1][1, 2, 1][1, 3, 3, 1][1, 4, 6, 4, 1][1, 5, 10, 10, 5, 1]

资源链接
Python 深入理解yield
廖雪峰的python教程-生成器
[推荐阅读]Python Generators(生成器)——yield关键字

0 0
原创粉丝点击