生成器方法漫谈(generator function)

生成器方法(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()不会产生错误。

你可能感兴趣的:(generator)