我们相信:世界是美好的,你是我也是。平行空间的世界里面,不同版本的生活也在继续...

生成器在很多高级语言里面都存在,使用方式也都差不多。本文聚焦于python大蟒蛇中的生成器generator中的yield关键字。那么,generator和普通的函数有何区别呢?yieldreturn又有什么区别呢?这就是苏南大叔在本文中要讨论的内容。

苏南大叔:如何正确使用python中的生成器里的yield关键字操作? - python-yield
如何正确使用python中的生成器里的yield关键字操作?(图4-1)

苏南大叔的“程序如此灵动”技术博客,记录苏南大叔的代码感想感悟。测试环境:win10python@3.11.0

概述

概述一下generatoryield,其实和大家已经熟知的functionreturn的作用非常类似。区别就在于:

  • generator是使用了yield关键字的function
  • generator获取数据是使用next(g)函数,它的数据并不是一次性获得的。而是通过next(g)一步一步获得的。
  • yield执行的时候,仅仅返回的是一个数。下次next(g)的时候,代码将从上一次yield的后一条语句继续执行。
  • 一个generator里面可以有多个yield,多个yield的数据返回规则不同也是可以的。

代码一,最简单方式【next(g)】

在下面的代码中,

  • 要明白rang(n)的范围是[0,n),代码传入参数2的话,只会涉及01两个数。
  • 因为只涉及两个数,同时也只有一个yield,所以next(g)也只能执行两次,以返回对应的结果。

测试代码如下:

def generater_sn(n):
    for i in range(n):      # [0,n)
        print('内部输出:生成之前...')
        yield i+1
        print('内部输出:生成之后...')

g = generater_sn(2)
result = next(g)
print("yield返回:",result)
print("外部输出:第一次生成")
result = next(g)
print("yield返回:",result)
print("外部输出:第二次生成")

输出:

内部输出:生成之前...
yield返回: 1
外部输出:第一次生成


内部输出:生成之后...
内部输出:生成之前...
yield返回: 2
外部输出:第二次生成

苏南大叔:如何正确使用python中的生成器里的yield关键字操作? - next-try
如何正确使用python中的生成器里的yield关键字操作?(图4-2)

代码一后续:异常控制

如果仔细查看上一个代码的输出的话,还差一个“内部输出:生成之后...”并没有被输出。但是如果继续next(g)的话,因为并没有数据可以被yield了,所以会得到一个异常信息StopIteration

这里继续上一个代码的输出,第三次执行next(g)

try:
    print("try 第三次生成 之前...")
    result = next(g) # 这里就发生异常了,后续并不执行,但是意外带出了yield语句之后未被执行的部分。
    print(result)
    print("try 第三次生成 之后...")
except StopIteration as e:
    print("发生异常 StopIteration exception")

输出:

try 第三次生成 之前...
内部输出:生成之后...
发生异常 StopIteration exception

可以看到:yield语句之后未被执行的部分,在这次失败的next(g)中被输出了。异常的捕获方式,可以参考文章:

代码二:正确的使用方式【for in循环】

def generater_sn(n):
    for i in range(n):      # [0,n)
        print('内部输出:生成之前...')
        yield i+1
        print('内部输出:生成之后...')

g = generater_sn(2)
for result in g:
    print(result)
    print("#############\r\n")

输出:

内部输出:生成之前...
1
#############

内部输出:生成之后...
内部输出:生成之前...
2
#############

内部输出:生成之后...

全部该输出的内容,都被输出了,并且没有任何异常信息。这个【for】使用方式相比较而已,更靠谱一点。但是却脱离了“生成器”迭代的初衷了。

苏南大叔:如何正确使用python中的生成器里的yield关键字操作? - for-in
如何正确使用python中的生成器里的yield关键字操作?(图4-3)

代码三:多重yield

这个情况下,yield就比return似乎先进多了,可以多次yield,并且数据生成规则还能保持不同。测试代码:

def generater_sn(n):
    for i in range(n):
        print('第一个yield准备...')
        yield i
        print('第二个yield准备...')
        yield i + 10
        print('双重yield完成...')


g = generater_sn(2)
for result in g:
    print(result)
    print("#############\r\n")

输出:

第一个yield准备...
0
#############

第二个yield准备...
10
#############

双重yield完成...


第一个yield准备...
1
#############

第二个yield准备...
11
#############

双重yield完成...

苏南大叔:如何正确使用python中的生成器里的yield关键字操作? - 多个yield
如何正确使用python中的生成器里的yield关键字操作?(图4-4)

与普通函数对比

def s():
    for i in range(10):
        yield i * i

def n():
    for i in range(10):
        return i * i
    
ss = s()
nn = n()
sss = next(ss)

print(type(s),type(ss),type(sss))
print(type(n),type(nn))

输出:

<class 'function'> <class 'generator'> <class 'int'>
<class 'function'> <class 'int'>

在代码中,并没有显式的出现生成器generator的字样,它和普通的函数是一样使用def进行定义的。只有当代码中出现yield字样的时候,才能把这个函数看成一个生成器。其返回值类型是:<class 'generator'>

相关文章

结论

从代码中可以看到:

  • yield是返回数据的标志性代码,但是只是暂停代码,并不会中断代码的执行。return会中断并返回。
  • next(g)就是推动yield流程进行下一步的代码,next(g)一次,就执行到下一个yield处。
  • 多个yield就可以返回不同规则的数据。
  • next(g)控制不好的话,就会引发异常。但是for result in g这种方式,就不会引发异常。

结束语

在目前很多高级编程语言里面,都有yield的身影,是单个函数分次返回多个数值的比较好的节省内存的方式。更多python相关经验文章,请参考苏南大叔的博客:

如果本文对您有帮助,或者节约了您的时间,欢迎打赏瓶饮料,建立下友谊关系。
本博客不欢迎:各种镜像采集行为。请尊重原创文章内容,转载请保留作者链接。

 【福利】 腾讯云最新爆款活动!1核2G云服务器首年50元!

 【源码】本文代码片段及相关软件,请点此获取更多信息

 【绝密】秘籍文章入口,仅传授于有缘之人   python