# 深入理解Python中的生成器

### 生成器(generator)概念

Python

 1 2 3 4 5 6 7 8 9 10 11 >>> gen = (x**2 for x in range(5)) >>> gen at 0x0000000002FB7B40> >>> for g in gen: ...   print(g, end=‘-‘) ... 0–1–4–9–16– >>> for x in [0,1,2,3,4,5]: ...   print(x, end=‘-‘) ... 0–1–2–3–4–5–

yield 的作用就是把一个函数变成一个 generator，带有 yield 的函数不再是一个普通函数，Python 解释器会将其视为一个 generator。

Python

 1 2 3 4 5 6 7 8 9 10 11 def odd():     n=1     while True:         yield n         n+=2 odd_num = odd() count = 0 for o in odd_num:     if count >=5: break     print(o)     count +=1

Python

 1 2 3 4 5 6 7 8 9 10 11 class Iter:     def __init__(self):         self.start=–1     def __iter__(self):         return self     def __next__(self):         self.start +=2         return self.start I = Iter() for count in range(5):     print(next(I))

Python

 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 >>> from collections import Iterable >>> from collections import Iterator >>> isinstance(odd_num, Iterable) True >>> isinstance(odd_num, Iterator) True >>> iter(odd_num) is odd_num True >>> help(odd_num) Help on generator object:   odd = class generator(object) |  Methods defined here: | |  __iter__(self, /) |      Implement iter(self). | |  __next__(self, /) |      Implement next(self). ......

### yield 与 return

Python

 1 2 3 4 5 6 7 8 9 10 11 >>> def g1(): ...     yield 1 ... >>> g=g1() >>> next(g)    #第一次调用next(g)时，会在执行完yield语句后挂起，所以此时程序并没有执行结束。 1 >>> next(g)    #程序试图从yield语句的下一条语句开始执行，发现已经到了结尾，所以抛出StopIteration异常。 Traceback (most recent call last):   File ““, line 1, in StopIteration >>>

Python

 1 2 3 4 5 6 7 8 9 10 11 12 >>> def g2(): ...     yield ‘a’ ...     return ...     yield ‘b’ ... >>> g=g2() >>> next(g)    #程序停留在执行完yield ‘a’语句后的位置。 ‘a’ >>> next(g)    #程序发现下一条语句是return，所以抛出StopIteration异常，这样yield ‘b’语句永远也不会执行。 Traceback (most recent call last):   File ““, line 1, in StopIteration

Python

 1 2 3 4 5 6 7 8 9 10 11 >>> def g3(): ...     yield ‘hello’ ...     return ‘world’ ... >>> g=g3() >>> next(g) ‘hello’ >>> next(g) Traceback (most recent call last):   File ““, line 1, in StopIteration: world

### 生成器支持的方法

Python

 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 >>> help(odd_num) Help on generator object:   odd = class generator(object) |  Methods defined here: ...... |  close(...) |      close() -> raise GeneratorExit inside generator. | |  send(...) |      send(arg) -> send ‘arg’ into generator, |      return next yielded value or raise StopIteration. | |  throw(...) |      throw(typ[,val[,tb]]) -> raise exception in generator, |      return next yielded value or raise StopIteration. ......

close()

Python

 1 2 3 4 5 6 7 8 9 10 11 12 13 >>> def g4(): ...     yield 1 ...     yield 2 ...     yield 3 ... >>> g=g4() >>> next(g) 1 >>> g.close() >>> next(g)    #关闭后，yield 2和yield 3语句将不再起作用 Traceback (most recent call last):   File ““, line 1, in StopIteration

send()

Python

 1 2 3 4 5 6 7 8 9 10 11 12 13 def gen():     value=0     while True:         receive=yield value         if receive==‘e’:             break         value = ‘got: %s’ % receive   g=gen() print(g.send(None))     print(g.send(‘aaa’)) print(g.send(3)) print(g.send(‘e’))

3. 通过g.send(3)，会重复第2步，最后输出结果为”got: 3″
4. 当我们g.send(‘e’)时，程序会执行break然后推出循环，最后整个函数执行完毕，所以会得到StopIteration异常。

Python

 1 2 3 4 5 6 7 0 got: aaa got: 3 Traceback (most recent call last): File “h.py”, line 14, in   print(g.send(‘e’)) StopIteration

throw()

throw()后直接跑出异常并结束程序，或者消耗掉一个yield，或者在没有下一个yield的时候直接进行到程序的结尾。

Python

 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 def gen():     while True:         try:             yield ‘normal value’             yield ‘normal value 2’             print(‘here’)         except ValueError:             print(‘we got ValueError here’)         except TypeError:             break   g=gen() print(next(g)) print(g.throw(ValueError)) print(next(g)) print(g.throw(TypeError))

Python

 1 2 3 4 5 6 7 8 normal value we got ValueError here normal value normal value 2 Traceback (most recent call last):   File “h.py”, line 15, in     print(g.throw(TypeError)) StopIteration

1. print(next(g))：会输出normal value，并停留在yield ‘normal value 2’之前。
2. 由于执行了g.throw(ValueError)，所以会跳过所有后续的try语句，也就是说yield ‘normal value 2’不会被执行，然后进入到except语句，打印出we got ValueError here。然后再次进入到while语句部分，消耗一个yield，所以会输出normal value。
3. print(next(g))，会执行yield ‘normal value 2’语句，并停留在执行完该语句后的位置。
4. g.throw(TypeError)：会跳出try语句，从而print(‘here’)不会被执行，然后执行break语句，跳出while循环，然后到达程序结尾，所以跑出StopIteration异常。

Python

 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 def flatten(nested):       try:         #如果是字符串，那么手动抛出TypeError。         if isinstance(nested, str):             raise TypeError         for sublist in nested:             #yield flatten(sublist)             for element in flatten(sublist):                 #yield element                 print(‘got:’, element)     except TypeError:         #print(‘here’)         yield nested   L=[‘aaadf’,[1,2,3],2,4,[5,[6,[8,[9]],‘ddf’],7]] for num in flatten(L):     print(num)

### 总结

1. 按照鸭子模型理论，生成器就是一种迭代器，可以使用for进行迭代。
2. 第一次执行next(generator)时，会执行完yield语句后程序进行挂起，所有的参数和状态会进行保存。再一次执行next(generator)时，会从挂起的状态开始往后执行。在遇到程序的结尾或者遇到StopIteration时，循环结束。
3. 可以通过generator.send(arg)来传入参数，这是协程模型。
4. 可以通过generator.throw(exception)来传入一个异常。throw语句会消耗掉一个yield。可以通过generator.close()来手动关闭生成器。
5. next()等价于send(None)
1
3 收藏

· 2 ·