yield

generator

在python的函数(function)定义中,只要出现了yield表达式(Yield expression),那么事实上定义的是一个generator function,调用这个generator function返回值是一个generator。这根普通的函数调用有所区别。

示例代码1:返回值类型
1
2
3
4
5
6
7
8
9
10
11
def gen_generator():
yield 1

def gen_value():
return 1

if __name__ == '__main__':
ret = gen_generator()
print(ret, type(ret)) # <generator object gen_generator at 0x02645648> <type 'generator'>
ret = gen_value()
print(ret, type(ret)) # 1 <type 'int'>

输出结果

1
2
<generator object gen_generator at 0x7f84244ab3b8> <class 'generator'>
1 <class 'int'>

两者返回同样的值,但是两者返回的最终类型却不一样,return 返回的是int基本数据类型,yield返回的却是generator


示例代码2:next调用
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
def gen_example():
print('before any yield')
yield ('first yield')
print('between yields')
yield ('second yield')
print('no yield anymore')

# 测试代码
gen = gen_example()
print('第1次调用:')
next(gen) # 第1次调用next
print('第2次调用:')
next(gen) # 第2次调用next
print('第3次调用:')
next(gen) # 第3次调用next

第一次调用 next() 输出:before any yield
第二次调用 next() 输出:between yields
第三次调用 next() 输出:抛出错误异常

调用过程分析

  • 调用gen example方法并没有输出任何内容,说明函数体的代码尚未开始执行。当调用generator的next方法,generator会执行到yield 表达式处,返回yield表达式的内容,然后暂停(挂起)在这个地方,所以第一次调用next打印第一句并返回“first yield”。 暂停意味着方法的局部变量,指针信息,运行环境都保存起来,直到下一次调用next方法恢复。第二次调用next之后就暂停在最后一个yield,再次调用next()方法,则会抛出StopIteration异常。 

示例代码3:for

for语句能自动捕获StopIteration异常,所以generator(本质上是任何iterator)较为常用的方法是在循环中使用:

1
2
3
4
5
6
7
def generator_example():
yield 1
yield 2


for e in generator_example():
print(e)

输出结果:1 2

小结,generator function产生的generator与普通的function有什么区别呢?

  • (1)function每次都是从第一行开始运行,而generator从上一次yield开始的地方运行
  • (2)function调用一次返回一个(一组)值,而generator可以多次返回
  • (3)function可以被无数次重复调用,而一个generator实例在yield最后一个值 或者return之后就不能继续调用了