python yield 详解

  1. 简单的例子
  2. for循环
  3. with语句
  4. yield from
  5. 取返回值
    1. 使用StopIteration

要理解yield,首先要清楚在有yield的函数,在执行的时候,其实返回的一个生成器对象。然后通过next()执行到yield语句停下,并返回yield所跟的值,然后一直等待next()的执行

  • iterable 可迭代对象, 实现了__iter__ 的对象
  • iterator 迭代器, 实现了 __iter____next__ 的对象
  • generator 生成器
  • yield __next__() 方法的优雅形式

简单的例子

def foo():
    print("starting...")
    while True:
        res = yield 4
        print("res:",res)
g = foo()
print(next(g))
print("*"*20)
print(next(g))

输出:

starting...
4
********************
res: None
4

流程是:

  1. g = foo()得到的是生成器对象,还并没有执行。
  2. next((g))执行g生成器知道yield语句,并返回4。
  3. 打印*
  4. next(g)继续执行,但是没有send()给出值,所有res为None

如果想要让res获得值,那么将第二个next(g)换成next(g,5),这样res就可以获得值5。或者g.send(5)也是一样。

for循环

如果一次次的调用next()未免也太过麻烦了,大多数情况可以使用for()来遍历生成器。也样也可避免使用StopIteration异常来停止遍历。

with语句

使用上下文管理器

from contextlib import contextmanager

class Query(object):
def __init__(self, name): self.name = name
def query(self): print('Query info about %s...' % self.name)

@contextmanager def create_query(name): print('Begin') q = Query(name) yield q print('End')

with create_query('Bob') as q: q.query()

输出:

Begin
Query info about Bob...
End

yield from

看个例子

def g1():     
     yield range(5)
def g2():
     yield from range(5)

it1 = g1() it2 = g2() for x in it1: print(x)
for x in it2: print(x)

输出:

range(0, 5)
0
1
2
3
4

可以知道yield返回的是可迭代对象,而yield from返回的是迭代器执行后每个item。

所以yield from iterable的本质就是for item in iterable: yield item

取返回值

正常情况下,迭代器是无法获取到返回值的,但是可以通过两种方式获取

  1. 使用StopIteration异常
  2. 使用类,然后取类的变量

使用StopIteration

def fib(max):
  n, a, b = 0, 0, 1
  while n < max:
    yield b
    a, b = b, a + b
    n = n + 1
  return 'done'
# 捕获Generator的返回值
g = fib(6)
while True:
  try:
    x=next(g)
    print('g=',x)
  except StopIteration as e:
    print('Generrator return value:', e.value)
    break

转载请注明来源,欢迎对文章中的引用来源进行考证,欢迎指出任何有错误或不够清晰的表达。可以在下面评论区评论,也可以邮件至 365433079@qq.com