日韩无码专区无码一级三级片|91人人爱网站中日韩无码电影|厨房大战丰满熟妇|AV高清无码在线免费观看|另类AV日韩少妇熟女|中文日本大黄一级黄色片|色情在线视频免费|亚洲成人特黄a片|黄片wwwav色图欧美|欧亚乱色一区二区三区

RELATEED CONSULTING
相關(guān)咨詢
選擇下列產(chǎn)品馬上在線溝通
服務(wù)時(shí)間:8:30-17:00
你可能遇到了下面的問(wèn)題
關(guān)閉右側(cè)工具欄

新聞中心

這里有您想知道的互聯(lián)網(wǎng)營(yíng)銷解決方案
PythonYieldGenerator詳解

本文將由淺入深詳細(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)容。

創(chuàng)新互聯(lián)公司堅(jiān)持“要么做到,要么別承諾”的工作理念,服務(wù)領(lǐng)域包括:成都做網(wǎng)站、網(wǎng)站設(shè)計(jì)、企業(yè)官網(wǎng)、英文網(wǎng)站、手機(jī)端網(wǎng)站、網(wǎng)站推廣等服務(wù),滿足客戶于互聯(lián)網(wǎng)時(shí)代的芝罘網(wǎng)站設(shè)計(jì)、移動(dòng)媒體設(shè)計(jì)的需求,幫助企業(yè)找到有效的互聯(lián)網(wǎng)解決方案。努力成為您成熟可靠的網(wǎng)絡(luò)建設(shè)合作伙伴!

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)    #  
  18.  
  19.     ret = gen_value() 
  20.  
  21.     print ret, type(ret)    # 1   

從上面的代碼可以看出,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()   ?。?nbsp;第二次調(diào)用next 
  24.  
  25. between yields 
  26.  
  27. 'second yield' 
  28.  
  29. >>> gen.next()    # 第三次調(diào)用next 
  30.  
  31. no yield anymore 
  32.  
  33. Traceback (most recent call last): 
  34.  
  35.   File "", line 1, in  
  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.  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ì)于每一條記錄,不同部門(mén)的人可能處理方式不同,但是我們可以提供一個(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

網(wǎng)頁(yè)名稱:PythonYieldGenerator詳解
文章網(wǎng)址:http://www.5511xx.com/article/cdpggps.html