Python生成器

 

生成器能够说是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的运行过程(如下为我根据测试程序结果推断的):

  1. 第一层,运行flatten([[0,1,2],3,4,[[5,6]],7]),展开后sublist为[0,1,2],进入第二层
  2. 第二层,运行flatten([0,1,2]),展开后sublist为0,进入第三层
  3. 第三层,运行flatten(0),代码试图对0进行展开,Python抛出TypeError,由代码捕获到,生成数据0,并返回生成器
  4. 返回第二层,对生成器进行迭代(含数据0),生成数据0,函数冻结,并返回生成器
  5. 返回第一层,对生成器进行迭代(含数据0),生成数据0,函数回到第二层的冻结点继续执行。

next()方法

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(value)方法

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()方法须要注意的有两点:

  1. 经过send()方法传入生成器的值,由n = (yield value)的形式被生成器内部接收,n即为传进来的参数的值。而若是未调用send()方法,n的值为None。
  2. send()方法不能在生成器刚初始化完成的时候调用,也就说必需要等到yield函数至少执行1次以后,才能调用send()方法,这是由于send()会在内部使生成器挂起,因此必需要求yield已经执行过,若是去掉上述代码中的第一个r.next(),则会获得错误:TypeError: can't send non-None value to a just-started generator。若是必定要在刚刚启动的生成器使用send()方法,能够采用send(None)来解决。
相关文章
相关标签/搜索