About
RSS

Bit Focus


小记 Python yield 与生成器

    yield 是 Python 中的一个关键字, 这个关键字比较特殊, 用于在任何表达式前, 但它不仅会对其后的表达式有影响, 对整个函数上下文都有影响. 实际上, 凡是在函数体中出现了 yield 关键字, Python 都会对此函数特殊处理, 调用这个函数不再返回值, 而是一个生成器对象.
    比如
def f():
    yield 1

g = f()
print type(g)
其结果是
<type 'generator'>
    如果要获取产生器产生的值, 则需要调用产生器对象的 next 函数
def f():
    yield 1

g = f()
print g.next()
    如果在函数中, 出现多个 yield, 或者函数的执行过程中反复路过某个 yield, 那么 next 每次调用会得到下一个产生的值, 比如
def f():
    yield 1
    yield 2

g = f()
print g.next(),
print g.next()
    得到的会是 1 2. 或如下的循环
def f():
    for x in range(3):
        yield x * 2

g = f()
print g.next(),
print g.next(),
print g.next()
    该循环中反复产生值, 每次产生一个就被传递给 next 函数作为返回值... 当然这样来说是不准确地, 如果上面 for 循环如果跑得太快, 那样会疾速产生值导致 next 函数应接不暇, 这样会有诡异的同步问题. 所以正确的语义应该是
  • 当生成器函数执行到包含 yield 的表达式时, 函数挂起, 并将 yield 之后的表达式作为返回值传递给 next 函数调用
  • 下一次 next 函数调用又会驱动该生成器的函数体继续执行此后的语句, 直到遇见下一个 yield 再次挂起并生成一个值交给 next
  • 如果某次 next 调用驱动了生成器继续执行, 而此后函数正常结束的话, 那么不会有任何值传递给 next 函数, 同时, 生成器会抛出 StopIteration 异常
    对于最后一点, 下面这个例子可作为参考
def f():
    yield 1

g = f()
print g.next(),
print g.next()
    另外生成器中如果出现 return, 它必须是一句空返回, 即 return 之后不允许跟任何表达式, 这也是限制之一.

    从下面这个例子可以更好地窥探生成器的执行模式
def f():
    print 'a'
    yield 1
    print 'b'
    yield 2
    print 'c'

g = f()
print 'start'
print g.next()
print 'next'
print g.next()
print 'end'
    输出结果为
start
a
1
next
b
2
end
    也就是说当第一次调用了 next 之后, f 函数体的执行就挂起了, 等着下一次调用 next. 另外, f 中最后一句应该输出的 c 并没有输出, 因为第二次生成器给出了 2 这个值之后就一直处于挂起状态.
    如果要大慈大悲地让最后一句也执行, 那么需要再加一句 next 调用
def f():
    print 'a'
    yield 1
    print 'b'
    yield 2
    print 'c'

g = f()
print 'start'
print g.next()
print 'next'
print g.next()
print 'end'
g.next()

Permanent Link: ?p=502 Load full text

Post tags:

 Python
 yield
 Generator


. Back to Bit Focus
NijiPress - Copyright (C) Neuron Teckid @ Bit Focus
About this site