成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

Python Yield Generator詳解

開(kāi)發(fā) 后端 開(kāi)發(fā)工具
本文將由淺入深詳細(xì)介紹yield以及generator,包括以下內(nèi)容:什么generator,生成generator的方法,generator的特點(diǎn),generator基礎(chǔ)及高級(jí)應(yīng)用場(chǎng)景,generator使用中的注意事項(xiàng)。本文不包括enhanced generator即pep342相關(guān)內(nèi)容。

Python Yield Generator詳解

本文將由淺入深詳細(xì)介紹yield以及generator,包括以下內(nèi)容:什么generator,生成generator的方法,generator的特點(diǎn),generator基礎(chǔ)及高級(jí)應(yīng)用場(chǎng)景,generator使用中的注意事項(xiàng)。本文不包括enhanced generator即pep342相關(guān)內(nèi)容。

generator基礎(chǔ)

在python的函數(shù)(function)定義中,只要出現(xiàn)了yield表達(dá)式(Yield expression),那么事實(shí)上定義的是一個(gè)generator function, 調(diào)用這個(gè)generator function返回值是一個(gè)generator。這根普通的函數(shù)調(diào)用有所區(qū)別,F(xiàn)or example:

  1. def gen_generator(): 
  2.  
  3.     yield 1 
  4.  
  5.   
  6.  
  7. def gen_value(): 
  8.  
  9.     return 1 
  10.  
  11.      
  12.  
  13. if __name__ == '__main__'
  14.  
  15.     ret = gen_generator() 
  16.  
  17.     print ret, type(ret)    #<generator object gen_generator at 0x02645648> <type 'generator'
  18.  
  19.     ret = gen_value() 
  20.  
  21.     print ret, type(ret)    # 1 <type 'int' 

從上面的代碼可以看出,gen_generator函數(shù)返回的是一個(gè)generator實(shí)例,generator有以下特別:

  • 遵循迭代器(iterator)協(xié)議,迭代器協(xié)議需要實(shí)現(xiàn)__iter__、next接口
  • 能過(guò)多次進(jìn)入、多次返回,能夠暫停函數(shù)體中代碼的執(zhí)行

下面看一下測(cè)試代碼:

  1. >>> def gen_example(): 
  2.  
  3. ...     print 'before any yield' 
  4.  
  5. ...     yield 'first yield' 
  6.  
  7. ...     print 'between yields' 
  8.  
  9. ...     yield 'second yield' 
  10.  
  11. ...     print 'no yield anymore' 
  12.  
  13. ... 
  14.  
  15. >>> gen = gen_example() 
  16.  
  17. >>> gen.next()   ?。?nbsp;***次調(diào)用next 
  18.  
  19. before any yield 
  20.  
  21. 'first yield' 
  22.  
  23. >>> gen.next()    # 第二次調(diào)用next 
  24.  
  25. between yields 
  26.  
  27. 'second yield' 
  28.  
  29. >>> gen.next()   ?。?nbsp;第三次調(diào)用next 
  30.  
  31. no yield anymore 
  32.  
  33. Traceback (most recent call last): 
  34.  
  35.   File "<stdin>", line 1, in <module> 
  36.  
  37. StopIteratio  

調(diào)用gen example方法并沒(méi)有輸出任何內(nèi)容,說(shuō)明函數(shù)體的代碼尚未開(kāi)始執(zhí)行。當(dāng)調(diào)用generator的next方法,generator會(huì)執(zhí)行到y(tǒng)ield 表達(dá)式處,返回yield表達(dá)式的內(nèi)容,然后暫停(掛起)在這個(gè)地方,所以***次調(diào)用next打印***句并返回“first yield”。 暫停意味著方法的局部變量,指針信息,運(yùn)行環(huán)境都保存起來(lái),直到下一次調(diào)用next方法恢復(fù)。第二次調(diào)用next之后就暫停在***一個(gè)yield,再次調(diào)用next()方法,則會(huì)拋出StopIteration異常。

因?yàn)閒or語(yǔ)句能自動(dòng)捕獲StopIteration異常,所以generator(本質(zhì)上是任何iterator)較為常用的方法是在循環(huán)中使用:

  1. def generator_example(): 
  2.  
  3.     yield 1 
  4.  
  5.     yield 2 
  6.  
  7.   
  8.  
  9. if __name__ == '__main__'
  10.  
  11.     for e in generator_example(): 
  12.  
  13.         print e 
  14.  
  15.         # output 1 2  

generator function產(chǎn)生的generator與普通的function有什么區(qū)別呢?

  1. function每次都是從***行開(kāi)始運(yùn)行,而generator從上一次yield開(kāi)始的地方運(yùn)行
  2. function調(diào)用一次返回一個(gè)(一組)值,而generator可以多次返回
  3. function可以被無(wú)數(shù)次重復(fù)調(diào)用,而一個(gè)generator實(shí)例在yield***一個(gè)值 或者return之后就不能繼續(xù)調(diào)用了

在函數(shù)中使用Yield,然后調(diào)用該函數(shù)是生成generator的一種方式。另一種常見(jiàn)的方式是使用generator expression,F(xiàn)or example:

  1. >>> gen = (x * x for x in xrange(5)) 
  2.  
  3. >>> print gen 
  4.  
  5. <generator object <genexpr> at 0x02655710>  

generator應(yīng)用

generator基礎(chǔ)應(yīng)用

為什么使用generator呢,最重要的原因是可以按需生成并“返回”結(jié)果,而不是一次性產(chǎn)生所有的返回值,況且有時(shí)候根本就不知道“所有的返回值”。比如對(duì)于下面的代碼:

  1. RANGE_NUM = 100 
  2.  
  3.     for i in [x*x for x in range(RANGE_NUM)]: # ***種方法:對(duì)列表進(jìn)行迭代 
  4.  
  5.         # do sth for example 
  6.  
  7.         print i 
  8.  
  9.   
  10.  
  11.     for i in (x*x for x in range(RANGE_NUM)): # 第二種方法:對(duì)generator進(jìn)行迭代 
  12.  
  13.         # do sth for example 
  14.  
  15.         print i  

在上面的代碼中,兩個(gè)for語(yǔ)句輸出是一樣的,代碼字面上看來(lái)也就是中括號(hào)與小括號(hào)的區(qū)別。但這點(diǎn)區(qū)別差異是很大的,***種方法返回值是一個(gè)列表,第二個(gè)方法返回的是一個(gè)generator對(duì)象。隨著RANGE_NUM的變大,***種方法返回的列表也越大,占用的內(nèi)存也越大;但是對(duì)于第二種方法沒(méi)有任何區(qū)別。

我們?cè)賮?lái)看一個(gè)可以“返回”無(wú)窮多次的例子:

  1. def fib(): 
  2.  
  3.     a, b = 1, 1 
  4.  
  5.     while True
  6.  
  7.         yield a 
  8.  
  9.         a, b = b, a+b  

這個(gè)generator擁有生成無(wú)數(shù)多“返回值”的能力,使用者可以自己決定什么時(shí)候停止迭代。

generator高級(jí)應(yīng)用

使用場(chǎng)景一:

Generator可用于產(chǎn)生數(shù)據(jù)流, generator并不立刻產(chǎn)生返回值,而是等到被需要的時(shí)候才會(huì)產(chǎn)生返回值,相當(dāng)于一個(gè)主動(dòng)拉取的過(guò)程(pull),比如現(xiàn)在有一個(gè)日志文件,每行產(chǎn)生一條記錄,對(duì)于每一條記錄,不同部門的人可能處理方式不同,但是我們可以提供一個(gè)公用的、按需生成的數(shù)據(jù)流。

  1. def gen_data_from_file(file_name): 
  2.  
  3.     for line in file(file_name): 
  4.  
  5.         yield line 
  6.  
  7.   
  8.  
  9. def gen_words(line): 
  10.  
  11.     for word in (w for w in line.split() if w.strip()): 
  12.  
  13.         yield word 
  14.  
  15.   
  16.  
  17. def count_words(file_name): 
  18.  
  19.     word_map = {} 
  20.  
  21.     for line in gen_data_from_file(file_name): 
  22.  
  23.         for word in gen_words(line): 
  24.  
  25.             if word not in word_map: 
  26.  
  27.                 word_map[word] = 0 
  28.  
  29.             word_map[word] += 1 
  30.  
  31.     return word_map 
  32.  
  33.   
  34.  
  35. def count_total_chars(file_name): 
  36.  
  37.     total = 0 
  38.  
  39.     for line in gen_data_from_file(file_name): 
  40.  
  41.         total += len(line) 
  42.  
  43.     return total 
  44.  
  45.      
  46.  
  47. if __name__ == '__main__'
  48.  
  49.     print count_words('test.txt'), count_total_chars('test.txt' 

上面的例子來(lái)自08年的PyCon一個(gè)講座。gen_words gen_data_from_file是數(shù)據(jù)生產(chǎn)者,而count_words count_total_chars是數(shù)據(jù)的消費(fèi)者??梢钥吹?,數(shù)據(jù)只有在需要的時(shí)候去拉取的,而不是提前準(zhǔn)備好。另外gen_words中 (w for w in line.split() if w.strip()) 也是產(chǎn)生了一個(gè)generator。

使用場(chǎng)景二:

一些編程場(chǎng)景中,一件事情可能需要執(zhí)行一部分邏輯,然后等待一段時(shí)間、或者等待某個(gè)異步的結(jié)果、或者等待某個(gè)狀態(tài),然后繼續(xù)執(zhí)行另一部分邏輯。比如微服務(wù)架構(gòu)中,服務(wù)A執(zhí)行了一段邏輯之后,去服務(wù)B請(qǐng)求一些數(shù)據(jù),然后在服務(wù)A上繼續(xù)執(zhí)行。或者在游戲編程中,一個(gè)技能分成分多段,先執(zhí)行一部分動(dòng)作(效果),然后等待一段時(shí)間,然后再繼續(xù)。對(duì)于這種需要等待、而又不希望阻塞的情況,我們一般使用回調(diào)(callback)的方式。下面舉一個(gè)簡(jiǎn)單的例子:

  1. def do(a): 
  2.  
  3.      print 'do', a 
  4.  
  5.      CallBackMgr.callback(5, lambda a = a: post_do(a)) 
  6.  
  7. def post_do(a): 
  8.  
  9.     print 'post_do', a  

這里的CallBackMgr注冊(cè)了一個(gè)5s后的時(shí)間,5s之后再調(diào)用lambda函數(shù),可見(jiàn)一段邏輯被分裂到兩個(gè)函數(shù),而且還需要上下文的傳遞(如這里的參數(shù)a)。我們用yield來(lái)修改一下這個(gè)例子,yield返回值代表等待的時(shí)間。

  1. @yield_dec 
  2.  
  3. def do(a): 
  4.  
  5.      print 'do', a 
  6.  
  7.      yield 5 
  8.  
  9.      print 'post_do', a  

這里需要實(shí)現(xiàn)一個(gè)YieldManager, 通過(guò)yield_dec這個(gè)decrator將do這個(gè)generator注冊(cè)到Y(jié)ieldManager,并在5s后調(diào)用next方法。Yield版本實(shí)現(xiàn)了和回調(diào)一樣的功能,但是看起來(lái)要清晰許多。下面給出一個(gè)簡(jiǎn)單的實(shí)現(xiàn)以供參考:

  1. # -*- coding:utf-8 -*- 
  2.  
  3. import sys 
  4.  
  5. # import Timer 
  6.  
  7. import types 
  8.  
  9. import time 
  10.  
  11.   
  12.  
  13. class YieldManager(object): 
  14.  
  15.     def __init__(self, tick_delta = 0.01): 
  16.  
  17.         self.generator_dict = {} 
  18.  
  19.         # self._tick_timer = Timer.addRepeatTimer(tick_delta, lambda: self.tick()) 
  20.  
  21.   
  22.  
  23.     def tick(self): 
  24.  
  25.         cur = time.time() 
  26.  
  27.         for gene, t in self.generator_dict.items(): 
  28.  
  29.             if cur >= t: 
  30.  
  31.                 self._do_resume_genetator(gene,cur) 
  32.  
  33.   
  34.  
  35.     def _do_resume_genetator(self,gene, cur ): 
  36.  
  37.         try: 
  38.  
  39.             self.on_generator_excute(gene, cur) 
  40.  
  41.         except StopIteration,e: 
  42.  
  43.             self.remove_generator(gene) 
  44.  
  45.         except Exception, e: 
  46.  
  47.             print 'unexcepet error', type(e) 
  48.  
  49.             self.remove_generator(gene) 
  50.  
  51.   
  52.  
  53.     def add_generator(self, gen, deadline): 
  54.  
  55.         self.generator_dict[gen] = deadline 
  56.  
  57.   
  58.  
  59.     def remove_generator(self, gene): 
  60.  
  61.         del self.generator_dict[gene] 
  62.  
  63.   
  64.  
  65.     def on_generator_excute(self, gen, cur_time = None): 
  66.  
  67.         t = gen.next() 
  68.  
  69.         cur_time = cur_time or time.time() 
  70.  
  71.         self.add_generator(gen, t + cur_time) 
  72.  
  73.   
  74.  
  75. g_yield_mgr = YieldManager() 
  76.  
  77.   
  78.  
  79. def yield_dec(func): 
  80.  
  81.     def _inner_func(*args, **kwargs): 
  82.  
  83.         gen = func(*args, **kwargs) 
  84.  
  85.         if type(gen) is types.GeneratorType: 
  86.  
  87.             g_yield_mgr.on_generator_excute(gen) 
  88.  
  89.   
  90.  
  91.         return gen 
  92.  
  93.     return _inner_func 
  94.  
  95.   
  96.  
  97. @yield_dec 
  98.  
  99. def do(a): 
  100.  
  101.     print 'do', a 
  102.  
  103.     yield 2.5 
  104.  
  105.     print 'post_do', a 
  106.  
  107.     yield 3 
  108.  
  109.     print 'post_do again', a 
  110.  
  111.   
  112.  
  113. if __name__ == '__main__'
  114.  
  115.     do(1) 
  116.  
  117.     for i in range(1, 10): 
  118.  
  119.         print 'simulate a timer, %s seconds passed' % i 
  120.  
  121.         time.sleep(1) 
  122.  
  123.         g_yield_mgr.tick()  

注意事項(xiàng):

(1)Yield是不能嵌套的!

  1. def visit(data): 
  2.  
  3.     for elem in data: 
  4.  
  5.         if isinstance(elem, tuple) or isinstance(elem, list): 
  6.  
  7.             visit(elem) # here value retuened is generator 
  8.  
  9.         else
  10.  
  11.             yield elem 
  12.  
  13.              
  14.  
  15. if __name__ == '__main__'
  16.  
  17.     for e in visit([1, 2, (3, 4), 5]): 
  18.  
  19.         print e  

上面的代碼訪問(wèn)嵌套序列里面的每一個(gè)元素,我們期望的輸出是1 2 3 4 5,而實(shí)際輸出是1 2 5 。為什么呢,如注釋所示,visit是一個(gè)generator function,所以第4行返回的是generator object,而代碼也沒(méi)這個(gè)generator實(shí)例迭代。那么改改代碼,對(duì)這個(gè)臨時(shí)的generator 進(jìn)行迭代就行了。

  1. def visit(data): 
  2.  
  3.     for elem in data: 
  4.  
  5.         if isinstance(elem, tuple) or isinstance(elem, list): 
  6.  
  7.             for e in visit(elem): 
  8.  
  9.                 yield e 
  10.  
  11.         else
  12.  
  13.             yield elem  

或者在python3.3中 可以使用yield from,這個(gè)語(yǔ)法是在pep380加入的:

  1. def visit(data): 
  2.  
  3.      for elem in data: 
  4.  
  5.          if isinstance(elem, tuple) or isinstance(elem, list): 
  6.  
  7.              yield from visit(elem) 
  8.  
  9.          else
  10.  
  11.              yield elem  

(2)generator function中使用return

在python doc中,明確提到是可以使用return的,當(dāng)generator執(zhí)行到這里的時(shí)候拋出StopIteration異常。

  1. def gen_with_return(range_num): 
  2.  
  3.     if range_num < 0: 
  4.  
  5.         return 
  6.  
  7.     else
  8.  
  9.         for i in xrange(range_num): 
  10.  
  11.             yield i 
  12.  
  13.   
  14.  
  15. if __name__ == '__main__'
  16.  
  17.     print list(gen_with_return(-1)) 
  18.  
  19.     print list(gen_with_return(1))  

但是,generator function中的return是不能帶任何返回值的。

  1. def gen_with_return(range_num): 
  2.  
  3.      if range_num < 0: 
  4.  
  5.          return 0 
  6.  
  7.      else
  8.  
  9.          for i in xrange(range_num): 
  10.  
  11.              yield i  

上面的代碼會(huì)報(bào)錯(cuò):SyntaxError: ‘return’ with argument inside generator

參考

  • http://www.dabeaz.com/generators-uk/
  • https://www.python.org/dev/peps/pep-0380/
  • http://stackoverflow.com/questions/231767/what-does-the-yield-keyword-do
  • http://stackoverflow.com/questions/15809296/python-syntaxerror-return-with-argument-inside-generator 
責(zé)任編輯:龐桂玉 來(lái)源: Python開(kāi)發(fā)者
相關(guān)推薦

2023-12-25 14:50:39

Python迭代器

2013-01-30 10:12:14

Pythonyield

2012-11-23 14:25:10

IBMdW

2021-03-15 12:23:24

Pythonyield代碼

2010-03-04 13:37:20

Python yiel

2021-05-13 09:11:11

PythonGo編程

2020-10-25 20:05:29

Pythonyield開(kāi)發(fā)

2023-12-11 13:59:00

YieldPython生成器函數(shù)

2021-04-22 21:15:38

Generator函數(shù)生成器

2022-03-03 08:30:41

GeneratorES6函數(shù)

2024-11-19 13:20:55

2024-12-13 08:02:10

PythonGenerator懶加載

2009-06-29 08:59:05

hbm的generat

2019-08-29 09:11:38

Pythonyield語(yǔ)法

2009-06-29 08:58:06

Hibernate的g

2009-12-18 11:37:54

Ruby關(guān)鍵字yiel

2024-03-01 19:35:54

Mybatis開(kāi)發(fā)

2009-07-02 09:32:47

generator子元Hibernate

2010-03-17 18:38:53

Java編程語(yǔ)言

2020-04-20 08:22:41

SOC安全工具網(wǎng)絡(luò)攻擊
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)

主站蜘蛛池模板: 日韩福利片 | 亚洲成人精品免费 | 一区二区三区在线免费 | 一区二区三区日 | 亚洲成人国产精品 | 成人在线视频网 | 亚洲日韩中文字幕一区 | 日本在线看片 | 国产三级一区二区 | 国产一区二区三区 | 亚州成人 | h肉视频| 久国久产久精永久网页 | 亚洲国产精品久久久久秋霞不卡 | 可以在线观看av的网站 | 国产综合精品 | 亚洲综合热 | www.亚洲一区二区 | 国产在线播 | 精品国产乱码久久久久久图片 | 蜜桃av一区二区三区 | 99精品热视频 | 国产中文字幕网 | 欧美性吧 | 日韩av大片免费看 | 日韩精品一区二区三区中文字幕 | 午夜成人免费视频 | 国产在线观看一区二区三区 | 亚洲欧洲日韩精品 中文字幕 | 久久久久99 | 521av网站| 国产色婷婷久久99精品91 | 精品一区二区三区电影 | 麻豆久久久9性大片 | 99只有精品 | va精品 | 国精产品一品二品国精在线观看 | 久久久久国产一区二区三区四区 | 日韩中文字幕免费在线 | 天堂亚洲网 | 亚洲精品一区中文字幕乱码 |