生成器能够说是Python和其余语言较为不一样的地方,刚开始看到《Python基础教程》里的Python生成器内容时,仍是很不适应,由于之前学过的语言都没有这种特性,因此理解不能,好在通过反复阅读书上的内容和本身编写代码测试以后,终于感受理解到点上了,这里写篇Blog用来备忘。python
从概念上来讲,生成器就是用普通方法定义的迭代器,或者更直观点的讲法就是任何包含yield语句的函数都是一个生成器。看一个简单生成器的代码1:web
python def repeater(): for i in range(10): yield 1 print list(repeater())
数据结构
输出以下:ide
[1, 1, 1, 1, 1, 1, 1, 1, 1, 1]
这个生成器生成了一组长度为10,每一个元素都为1的数据,咱们最后将其转换成列表形式进行输出。函数
在这个例子中,若是用print来代替yield咱们也能获得『类似』的输出。可是yield的一个显著特征就是它不是返回任何值,而是产生值,代码1中,就是每次循环产生一个1。而当函数执行完以后,并不返回值,也不返回None(这一点很重要,若是只是简单地把yield当成print的一种变形就很容易犯这个错误),而是返回一个生成器,若是咱们直接输出代码1中的repeater()函数:测试
python print repeater()
大数据
将会获得:spa
<generator object repeater at 0x101a4e410>
就像上面所说的,生成器并不直接返回值,而是返回一个根据你的函数定义的生成器,当用相似list(generator)方法调用的时候,就能够根据相应的数据结构生成数据。code
再来看一个较为复杂的生成器代码2:对象
``` python def flatten(nested): try: for sublist in nested: for element in flatten(sublist): yield element except TypeError: yield nested
print list(flatten([[0,1,2],3,4,[[5,6]],7])) ```
代码2的输出为:
[0, 1, 2, 3, 4, 5, 6, 7]
代码2的做用就是将一棵树进行展开,采用了递归的思想,它的工做原理就是,若是当前的对象仍是能够展开的,那就继续展开,若是没法展开(Python中int是没法展开的),就会抛出TypeError这个异常,并且这个引起TypeError的值就是咱们但愿获得的值,因此捕获这个异常后生成值。
代码2一样来自《Python基础教程》,若是要深究起运行过程,则先须要理解生成器是可迭代的,是能够为做为for循环的展开对象的。而且,yield每次产生一个值,都会将函数冻结,下次则再从该冻结的点继续执行。拿第一个元素0举例来讲明代码2的运行过程(如下为我根据测试程序结果推断的):
next()方法的示例代码以下:
python def repeater(): while True: yield 1 r = repeater() print r.next()
以上代码会输出1,并且若是你继续调用r.next(),就会继续输出1。next()其实就是迭代器,读取生成器生成的下一个值,这里能够看出生成器相较于列表更有优点的一点就是:列表老是先将数据生成并储存起来,当须要用的时候就去读取,而生成器都是当须要使用的时候才生成数据,这一点在处理大数据量甚至是无限生成的数据时尤为有用。
next()方法还须要注意的一点是,对于yield n语句,next()返回的是n的值,而yield n自己的返回值为None,这一点在send()方法中还会再一次被提到。
send()方法容许外界向生成器内部传递数据,当调用send()方法时,内部将会挂起生成器,yield做为表达式而非语句使用,yield n将会有一个返回值,该值就是外界经过send()方法传递进来的值。send()方法的示例代码以下:
``` python # When you call next(), n is ever None. When you call send(num), yield is hold up, # so (yield value) return num def repeater(value): while True: n = (yield value)
if n is not None: value = n
r = repeater(11) print r.next() r.send(10) print r.next()
```
输出为:
11 10
能够看到在生成器初始化完后,调用next()获得的值是咱们初始化时传递进去的11,而当调用send()方法向生成器传了一个值10以后,在调用next()方法,获得就是后传进去的值10了。
send()方法须要注意的有两点:
TypeError: can't send non-None value to a just-started generator
。若是必定要在刚刚启动的生成器使用send()方法,能够采用send(None)来解决。