python 生成器yield

生成器概念

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

生成器的特点

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

认识生成器

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

def addlist(alist):
    for i in alist:
        # yield与return功能类似,返回本次迭代结果
        yield i + 1

alist = [1, 2, 3, 4]
for x in addlist(alist):
    print(x)

执行结果:

2
3
4
5

根据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 = 0
begin yield
1

# 第2次执行输出 a = 1, i = 1
end yield
begin yield
2

# 第3次执行输出 a = 2, i = 2
end yield
begin yield
4

# 第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   1
1   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 = 6
fn = 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
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值