生成器是Python新引入的概念,由于历史原因,它也叫做简单生成器。它和迭代器可能近几年来引入的最大的两个特征。但是,生成器的概念则要更高级一些,需要花费一些功夫才能理解它是如何工作的以及有什么用处。生成器可以帮助读者写出优雅的代码,当然,编写任何程序时不使用生成器也是可以的。
生成器是一种普通的函数语法定义的迭代器。它的工作方式可以用例子来很好地展现。
1、创建迭代器
创建一个生成器就象创建函数一样。接下来用一个例子说明生成器的知识。首先创建一个嵌套列表的函数。参数是一个例子,如下所示:
nested=[[1,2],[3,4],[5]]
def flattern(nested):
for sublist in nested:
for element in sublist:
yield element
print(element)
2、递归生成器
上诉创建的生成器只能处理两层嵌套,为了处理嵌套使用了两个for循环。哪如何处理任意层嵌套的呢?每层嵌套需要增加一个for循环,但因为不知道几层嵌套,所以必须把解决方案变得灵活。用递归能解决此问题。
def flatten(nested):
try:
for sublist in nested:
for element in flatten(sublist):
yield element
except TypeError:
yield nested
当flattern被调用时,有两种可能性:基本情况和需要递归的情况。
在基本情况中,函数被告知展开一个元素,这种情况下,for循环会引起一个TypeError异常,生成器会产生一个元素。
需要递归情况中,展开还是一个列表。程序会遍历所有的字列表,并对他们调用flattern,然后使用使用另一个for循环来产生被展开的字列表中的所有元素。
>>>list(flattern([[[1],2],3,4,[5,[6,7]],8]))
[1,2,3,4,5,6,7,8]
如果输入的函数类似于字符串的对象,迭代对结果产生干扰(但不会引发TypeError),为了处理这种情况,则必须在生成器中的开始添加一个检查语句。试着将传入的对象和一个字符拼接,看看会不会引发TypeError,这是检查一个对象是不是类似于字符串的最简单,最快捷的方法。下面是加入了检查语句的生成器:
def flatten(nested):
try:
try:nested+' '
except TypeError:pass
else:raise TypeError
for sublist in nested:
for element in flatten(sublist):
yield element
except TypeError:
yield nested
如果表达式nest+‘ ’引发了一个TypeError,它就会被忽略。然后如果没有引发TypeError,那么内层try语句中else子句就会引发一个TypeError异常。这就会按照原来的样子生成类似于字符串的对象(在except子句的外面).
例子展示:
>>>list(flatten(['foo',['bar',['baz']]]))
['foo','bar','baz']
3、通用生成器
如果到目前的所有例子你看懂了,哪应该或多或少地知道如何使用生气器。生成器是一个包含yield关键字的函数。当它被调用时,在函数体中的代码,而会返回一个迭代器。每次请求一个值,就会执行生成器中的代码,知道遇见一个yield或者return语句。yield语句意味着应该生成一个值。return语句意味着生成器要停止执行(不再生成任何东西,return语句只有一个生成器中使用时才能进行无参数调用)。
换句话说,生成器是由两部分组成:生成器的函数和生成器的迭代器。生成器的函数是用def语句定义的,包含yield的部分,生成器的迭代器是这个函数返回的部分。按一种不是很准确的说法。两个实体经常被当做一个,合起来叫做生成器。
>>>def simple_generator():
yield 1
. . .
>>>simple_generator
<function simple_generator at 153b44>
>>>simple_generator()
<genertor object at 1510b0>
生成器的函数返回迭代器可以像其他的迭代器那样使用。
4、生成器方法
生成器的新特征是在开始运行后为生成器提供值得能力。表现为生成器和“外部世界”进行交流的渠道,要注意下面两点。
(1)外部作用域访问生成器的send方法,就像访问next方法一样,只不过前者使用一个参数(要发送的‘’消息‘’————任意对象)。
(2)在内部则挂起生成器,yield现在作为表达式而不是语句使用,换句话说,当生成器重新运行的时候,yield方法返回一个值,也就是外部通过send发放发送的值。如果next方法被使用,那么yield方法返回None。
!!!使用send方法只有在生成器挂起之后才有意义(也就是说yield函数第一次被执行之后)。如果在此之前需要给生成器提供更多信息,那么只需要使用生成器函数的参数。如果真想对刚刚启动的生成器使用send方法,那么可以将None作为其参数进行调用。
例:
def repeater(value):
while True:
new=(yield value)
if new is not None:value=new
使用方法如下:
r=repeater(42)
r.next()
42
r.send("Hello,world!")
"Hello,world!"
生成器还有其他两个方法:
(3)throw方法用于在生成器内引发一个异常
(4)close方法用于停止生成器。
close方法也是建立在异常的基础上的。它在yield运行处引发一个GeneratorExit异常,所以如果需要在生成器内进行代码清理的话,则可以将yield语句放在try/finally语句中。
5、模拟生成器
生成器在旧版本中的python中是不可用的。下面介绍的使用普通函数模拟生成器。
先从生成器的代码开始。首先下面语句放在函数图的开始处:
resulr=[ ]
然后将下面这种形式的代码:
yield some_expression
用下面的语句替换:
result.append(some_expression)
最后,在函数的末尾,添加下面的语句:
return result
此方法适用于大多数的生成器(比如,不能用于一个无限的生成器,当然不能把它的值放入列表中).
下面是flattern生成器用普通函数的版本:
def flatten(nested):
result=[]
try:
try:nested+' '
except TypeError:pass
else:raise TypeError
for sublist in nested:
for element in flatten(sublist):
result.append(element)
except TypeError:
result.append(nested)
return result