Python之生成器

生成器是一种特殊的迭代器

1. 如何构造一个生成器

In [6]: li = [item for item in range(10)] 
   ...: print(li) 
   ...: ge = (item for item in range(10)) 
   ...: print(ge)                                                                             
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
<generator object <genexpr> at 0x7fee50aaf048>

可以看到上面的创建方式的不同来自于[ ] ( ),但创建出来的一个是列表,一个是生成器,我们可以使用next()方法和for循环来调用生成器

In [7]: ge = (item for item in range(4)) 
   ...: print(next(ge)) 
   ...: print(next(ge)) 
   ...: print(next(ge)) 
   ...: print(next(ge)) 
   ...: print(next(ge))                                                                       
0
1
2
3
---------------------------------------------------------------------------
StopIteration                             Traceback (most recent call last)
<ipython-input-7-af4a652793c7> in <module>
      4 print(next(ge))
      5 print(next(ge))
----> 6 print(next(ge))

StopIteration: 
In [8]: ge = (item for item in range(4)) 
   ...: for i in ge: 
   ...:     print(i) 
   ...:                                                                                       
0
1
2
3

2. 构造生成器的另一种方式

首先我们回顾下迭代器,前面说过生成器是一种特殊的迭代器
首先我们先使用迭代器实现斐波那契数列,然后在使用生成器来实现,来比较两者的不同
使用迭代器来实现

In [9]: class Fibo(object): 
   ...:     def __init__(self, count): 
   ...:         self.start = 0 
   ...:         self.end = 1 
   ...:         self.current = 0 
   ...:         self.count = count 
   ...:          
   ...:     def __next__(self): 
   ...:         if self.current <= self.count: 
   ...:             fibo = self.start 
   ...:             self.start, self.end = self.end, self.end+self.start 
   ...:             self.current += 1 
   ...:             return fibo 
   ...:         else: 
   ...:             raise StopIteration 
   ...:      
   ...:     def __iter__(self): 
   ...:         return self 
   ...:      
   ...: fibo = Fibo(10) 
   ...: for i in fibo: 
   ...:     print(i) 
   ...:                                                                                       
0
1
1
2
3
5
8
13
21
34
55

使用生成器来实现

In [10]: def Fibo(n): 
    ...:     count = 0 
    ...:     start, end = 0, 1 
    ...:     while count <= n: 
    ...:         fibo = start 
    ...:         start, end = end, start+end 
    ...:         count += 1 
    ...:         yield fibo 
    ...:     return 'None' 
    ...:  
    ...: fibo = Fibo(10) 
    ...: for i in fibo: 
    ...:     print(i) 
    ...:                                                                                      
0
1
1
2
3
5
8
13
21
34
55

3. 小结

  • 函数中使用了yield关键字的都是生成器
  • yield关键字的作用
    • yield起到了return的作用,返回它后面的值
    • yield保存了当前运行状态,暂停执行,也就是将函数挂起
  • 使用next()将函数唤醒,继续执行
  • python3中使用yield最终可以使用return来返回一个值,这个值需要捕获StopIteration,python2中不允许使用return返回一个返回值

4. 使用send()来唤醒

除了使用next()函数来唤醒生成器之外,send()函数也可以唤醒。使用send()函数的一个好处是可以向断点传入数据。

In [11]: def gene(): 
    ...:     count = 0 
    ...:     while count < 5: 
    ...:         yield count 
    ...:         count += 1 
    ...: g = gene() 
    ...: print(next(g)) 
    ...: print(next(g)) 
    ...: print(next(g)) 
    ...: print(next(g)) 
    ...: print(next(g)) 
    ...: print(next(g))                                                                       
0
1
2
3
4
---------------------------------------------------------------------------
StopIteration                             Traceback (most recent call last)
<ipython-input-11-fff2af188c57> in <module>
     10 print(next(g))
     11 print(next(g))
---> 12 print(next(g))

StopIteration: 
In [12]: def gene(): 
    ...:     count = 0 
    ...:     while count < 5: 
    ...:         temp = yield count 
    ...:         print(temp) 
    ...:         count += 1 
    ...:          
    ...: g = gene() 
    ...: print(next(g)) 
    ...: print(g.send("a")) 
    ...: print(g.send("b")) 
    ...: print(g.send("c")) 
    ...: print(g.send("d")) 
    ...: print(g.send("e")) 
    ...: print(g.send("f"))                                                                   
0
a
1
b
2
c
3
d
4
e
---------------------------------------------------------------------------
StopIteration                             Traceback (most recent call last)
<ipython-input-12-f42c13492dca> in <module>
     12 print(g.send("c"))
     13 print(g.send("d"))
---> 14 print(g.send("e"))
     15 print(g.send("f"))

StopIteration: 

使用send()函数来向temp变量来传递一个值,生成器在第一次执行时,碰到yield会挂起,而这时send()函数传递的值没有变量去接收,这就会报错,所以第一次的执行应该是next()函数,如果send()函数为第一次执行,那么,send()传递的变量应该为None。

In [13]: def gene(): 
    ...:     count = 0 
    ...:     while count < 3: 
    ...:         temp = yield count 
    ...:         print(temp) 
    ...:         count += 1 
    ...:          
    ...: g = gene() 
    ...: print(g.send("a")) # 第一次使用send()来执行 
    ...: print(g.send("b")) 
    ...: print(g.send("c"))                                                                   
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-13-8a4a1728c567> in <module>
      7 
      8 g = gene()
----> 9 print(g.send("a")) # 第一次使用send()来执行
     10 print(g.send("b"))
     11 print(g.send("c"))

TypeError: can't send non-None value to a just-started generator
In [14]: def gene(): 
    ...:     count = 0 
    ...:     while count < 3: 
    ...:         temp = yield count 
    ...:         print(temp) 
    ...:         count += 1 
    ...:          
    ...: g = gene() 
    ...: print(g.send(None)) # 第一次使用send()来执行,但是传递的参数是None 
    ...: print(g.send("b")) 
    ...: print(g.send("c"))                                                                   
0
b
1
c
2
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,937评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,503评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,712评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,668评论 1 276
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,677评论 5 366
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,601评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,975评论 3 396
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,637评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,881评论 1 298
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,621评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,710评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,387评论 4 319
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,971评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,947评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,189评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 44,805评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,449评论 2 342

推荐阅读更多精彩内容