生成器方法(generator function)作为一个可选特性在Python2.2中首次出现,2.3版本中内置支持了此特性,yield成为了关键字,生成器在后续版本中得到增强(比如增加了异常处理等特性)。C#2.0中也引入类似特性(迭代器),这两者之间有不少相似之处。本文针对IronPython 2.0 beta3进行讨论。
任何包含yield表达式的函数即为生成器方法,同时yield也只在定义一个生成器方法时使用。生成器方法是一个特殊的方法,当你调用生成器方法时,返回的不是像普通函数一样的单一值,而是返回了一个迭代器(相当于C#中的System.Collections.IEnumerator接口)。yield表达式会返回一个值:val = (yield i) ,val的值即为表达式的值。但是这个值并不是i,这个值是由调用生成器的send(value)方法指定的。如果不调用此方法或者调用send(None)方法,则返回值为None。而迭代器本身的返回值则是由yield语句(
注意yield表达式[val = (yield i)]和yield语句[yield i]的区别)指定的,当每次调用迭代器的next()/send()方法时,yield语句会立即返回一个值给调用方,同时会保存当前代码执行位置及现场状态,并暂停执行(此时yield表达式还未被执行,即val没被赋值)。当再次调用迭代器的方法时将从此位置(yield表达式)恢复执行直到再次遇到下一个yield表达式。
下面将根据生成器支持的操作方法具体介绍:
next()
>>>
def
generate_ints(N):
for
i
in
range(N):
yield
i
>>>
gen
=
generate_ints(
3
)
>>>
gen
<
generator object at
0x000000000000002B
>
>>>
gen.next()
0
>>>
gen.next()
1
>>>
gen.next()
2
>>>
gen.next()
Traceback (most recent call last):
File
"
<stdin>
"
, line
1
,
in
<
module
>
StopIteration: 引发类型为“IronPython.Runtime.Exceptions.StopIterationException
”的异常。
gen.next()调用后,generate_ints()开始执行,返回yield语句右侧的
i值,在yield表达式处暂停(本例忽略了表达式的值,只使用了yield语句)。当再次调用gen.next()后,从暂停处会继续执行直到下一个yield表达式。当我们第4次调用gen.next()时由于已经没有下一个yield表达式所以拋出一个StopIteration异常,指示迭代器结束。
send(value)
>>>
def
counter (maximum):
i
=
0
while
i
<
maximum:
val
=
(
yield
i)
#
如果设置了yield表达式的值,则将改变计数器的值
if
val
is
not
None:
i
=
val
else
:
i
+=
1
>>>
it
=
counter(
10
)
>>>
print
it.next()
0
>>>
print
it.send(
8
)
8
>>>
print
it.next()
9
>>>
print
it.next()
Traceback (most recent call last):
File
"
<stdin>
"
, line
1
,
in
<
module
>
StopIteration: 引发类型为“IronPython.Runtime.Exceptions.StopIterationException
”的异常。
>>>
我们可以看到yield表达式的返回值是由send(value)中的value指定的。next()/send()方法都能使生成器继续执行并返回yield语句右侧的值,不同点在于next()是不带参数的,所以也就不可以设置yield表达式的返回值(默认值为None)。我们也可以调用send(None),这样两者的功能是等价的。
注意:首次调用时,不可以用send()方法去赋一个非None值,因为这个值没有yield表达式去接收,否则会引发一个TypeError异常:
Traceback (most recent call last):
File
"
<stdin>
"
, line
1
,
in
<
module
>
TypeError: can
'
t send non-None value to a just-started generator
throw( type[, value[, traceback]])
throw(type, value=None, traceback=None)用来在生成器内部引发一个异常。异常在生成器暂停执行处由yield表达式引发。我们知道当生成器缺少yield表达式而结束的时候会拋出一个StopIteration异常而结束,我们也可以用throw()来传入一个异常给生成器,从而控制生成器的执行。当在生成器内部没有对传入的异常进行捕获处理时,异常将返回给调用者。
>>>
def
throw():
i
=
0
while
True:
i
+=
1
try
:
yield
i
except
StopIteration, v:
print
"
Caught StopIteration
"
#
这里是为了示例在生成器里如何捕获throw()传进的异常,
#
所以还把异常返回给调用方,结束生成器
raise
v
>>>
g
=
throw()
>>>
g.next()
1
>>>
g.next()
2
>>>
g.throw(StopIteration)
Caught StopIteration
Traceback (most recent call last):
File
"
<stdin>
"
, line
1
,
in
<
module
>
StopIteration
>>>
g.next()
Traceback (most recent call last):
File
"
<stdin>
"
, line
1
,
in
<
module
>
StopIteration: 引发类型为“IronPython.Runtime.Exceptions.StopIterationException
”的异常。
>>>
首先定义了一个无穷的生成器函数,在正常调用next()方法的情况下会一直有返回值,当调用了throw(StopIteration)传入一个结束迭代器异常后,于是这个异常被生成器捕获并返回给调用者使生成器结束,当再调用next()方法时,引发了StopIteration异常。
close()
有了上面的throw()方法的介绍后,现在理解close()就容易多了。此方法会在生成器暂停执行处抛出GeneratorExit异常。当在生成器内部捕获到此异常后必须重新抛出GeneratorExit或者StopIteration异常以使生成器正常结束,如果没有重新抛出而是返回一个值的话将触发RuntimeError异常。对一个生成器多次调用close()不会产生错误。