欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

由淺入深講解python中的yield與generator

 更新時(shí)間:2017年04月05日 08:37:20   作者:xybaby  
這篇文章主要由淺入深講解了python中yield與generator的相關(guān)資料,文中介紹的非常詳細(xì),對(duì)大家具有一定的參考價(jià)值,需要的朋友們下面來一起看看吧。

前言

本文將由淺入深詳細(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)容,這部分內(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:

def gen_generator():
 yield 1

def gen_value():
 return 1
 
if __name__ == '__main__':
 ret = gen_generator()
 print ret, type(ret) #<generator object gen_generator at 0x02645648> <type 'generator'>
 ret = gen_value()
 print ret, type(ret) # 1 <type 'int'>

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

generator有以下特別:

     •遵循迭代器(iterator)協(xié)議,迭代器協(xié)議需要實(shí)現(xiàn)__iter__ 、next接口

     •能過多次進(jìn)入、多次返回,能夠暫停函數(shù)體中代碼的執(zhí)行

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

>>> def gen_example():
... print 'before any yield'
... yield 'first yield'
... print 'between yields'
... yield 'second yield'
... print 'no yield anymore'
... 
>>> gen = gen_example()
>>> gen.next()   ?。?第一次調(diào)用next
before any yield
'first yield'
>>> gen.next()    # 第二次調(diào)用next
between yields
'second yield'
>>> gen.next()   ?。?第三次調(diào)用next
no yield anymore
Traceback (most recent call last):
 File "<stdin>", line 1, in <module>
StopIteratio

調(diào)用gen example方法并沒有輸出任何內(nèi)容,說明函數(shù)體的代碼尚未開始執(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)境都保存起來,直到下一次調(diào)用next方法恢復(fù)。第二次調(diào)用next之后就暫停在最后一個(gè)yield,再次調(diào)用next()方法,則會(huì)拋出StopIteration異常。 

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

def generator_example():
 yield 1
 yield 2

if __name__ == '__main__':
 for e in generator_example():
 print e
 # output 1 2

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

  (1)function每次都是從第一行開始運(yùn)行,而generator從上一次yield開始的地方運(yùn)行

  (2)function調(diào)用一次返回一個(gè)(一組)值,而generator可以多次返回

 ?。?)function可以被無數(shù)次重復(fù)調(diào)用,而一個(gè)generator實(shí)例在yield最后一個(gè)值 或者return之后就不能繼續(xù)調(diào)用了

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

  >>> gen = (x * x for x in xrange(5))
  >>> print gen
  <generator object <genexpr> at 0x02655710>

generator應(yīng)用

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

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

比如對(duì)于下面的代碼  

RANGE_NUM = 100
 for i in [x*x for x in range(RANGE_NUM)]: # 第一種方法:對(duì)列表進(jìn)行迭代
 # do sth for example
 print i

 for i in (x*x for x in range(RANGE_NUM)): # 第二種方法:對(duì)generator進(jìn)行迭代
 # do sth for example
 print i

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

我們?cè)賮砜匆粋€(gè)可以“返回”無窮多次的例子:

def fib():
 a, b = 1, 1
 while True:
 yield a
 a, b = b, a+b 

這個(gè)generator擁有生成無數(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)拉取的過程(pull),比如現(xiàn)在有一個(gè)日志文件,每行產(chǎn)生一條記錄,對(duì)于每一條記錄,不同部門的人可能處理方式不同,但是我們可以提供一個(gè)公用的、按需生成的數(shù)據(jù)流。

def gen_data_from_file(file_name):
 for line in file(file_name):
 yield line

def gen_words(line):
 for word in (w for w in line.split() if w.strip()):
 yield word

def count_words(file_name):
 word_map = {}
 for line in gen_data_from_file(file_name):
 for word in gen_words(line):
  if word not in word_map:
  word_map[word] = 0
  word_map[word] += 1
 return word_map

def count_total_chars(file_name):
 total = 0
 for line in gen_data_from_file(file_name):
 total += len(line)
 return total
 
if __name__ == '__main__':
 print count_words('test.txt'), count_total_chars('test.txt')

上面的例子來自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è)簡單的例子:

 def do(a):
 print 'do', a
 CallBackMgr.callback(5, lambda a = a: post_do(a))
 
 def post_do(a):
 print 'post_do', a

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

 @yield_dec
 def do(a):
 print 'do', a
 yield 5
 print 'post_do', a

這里需要實(shí)現(xiàn)一個(gè)YieldManager, 通過yield_dec這個(gè)decrator將do這個(gè)generator注冊(cè)到Y(jié)ieldManager,并在5s后調(diào)用next方法。Yield版本實(shí)現(xiàn)了和回調(diào)一樣的功能,但是看起來要清晰許多。

下面給出一個(gè)簡單的實(shí)現(xiàn)以供參考:

# -*- coding:utf-8 -*-
import sys
# import Timer
import types
import time

class YieldManager(object):
 def __init__(self, tick_delta = 0.01):
 self.generator_dict = {}
 # self._tick_timer = Timer.addRepeatTimer(tick_delta, lambda: self.tick())

 def tick(self):
 cur = time.time()
 for gene, t in self.generator_dict.items():
  if cur >= t:
  self._do_resume_genetator(gene,cur)

 def _do_resume_genetator(self,gene, cur ):
 try:
  self.on_generator_excute(gene, cur)
 except StopIteration,e:
  self.remove_generator(gene)
 except Exception, e:
  print 'unexcepet error', type(e)
  self.remove_generator(gene)

 def add_generator(self, gen, deadline):
 self.generator_dict[gen] = deadline

 def remove_generator(self, gene):
 del self.generator_dict[gene]

 def on_generator_excute(self, gen, cur_time = None):
 t = gen.next()
 cur_time = cur_time or time.time()
 self.add_generator(gen, t + cur_time)

g_yield_mgr = YieldManager()

def yield_dec(func):
 def _inner_func(*args, **kwargs):
 gen = func(*args, **kwargs)
 if type(gen) is types.GeneratorType:
  g_yield_mgr.on_generator_excute(gen)

 return gen
 return _inner_func

@yield_dec
def do(a):
 print 'do', a
 yield 2.5
 print 'post_do', a
 yield 3
 print 'post_do again', a

if __name__ == '__main__':
 do(1)
 for i in range(1, 10):
 print 'simulate a timer, %s seconds passed' % i
 time.sleep(1)
 g_yield_mgr.tick()

注意事項(xiàng):

(1)Yield是不能嵌套的!

def visit(data):
 for elem in data:
 if isinstance(elem, tuple) or isinstance(elem, list):
  visit(elem) # here value retuened is generator
 else:
  yield elem
  
if __name__ == '__main__':
 for e in visit([1, 2, (3, 4), 5]):
 print e

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

def visit(data):
 for elem in data:
 if isinstance(elem, tuple) or isinstance(elem, list):
  for e in visit(elem):
  yield e
 else:
  yield elem

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

 def visit(data):
 for elem in data:
  if isinstance(elem, tuple) or isinstance(elem, list):
  yield from visit(elem)
  else:
  yield elem

(2)generator function中使用return

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

def gen_with_return(range_num):
 if range_num < 0:
 return
 else:
 for i in xrange(range_num):
  yield i

if __name__ == '__main__':
 print list(gen_with_return(-1))
 print list(gen_with_return(1))

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

 def gen_with_return(range_num):
 if range_num < 0:
  return 0
 else:
  for i in xrange(range_num):
  yield i

上面的代碼報(bào)錯(cuò):SyntaxError: 'return' with argument inside generator

總結(jié)

以上就是這篇文章的全部內(nèi)容了,希望本文的內(nèi)容對(duì)大家的學(xué)習(xí)或者工作能帶來一定的幫助,如果有疑問大家可以留言交流,謝謝大家對(duì)腳本之家的支持。

相關(guān)文章

  • jupyter notebook oepncv 顯示一張圖像的實(shí)現(xiàn)

    jupyter notebook oepncv 顯示一張圖像的實(shí)現(xiàn)

    這篇文章主要介紹了jupyter notebook oepncv 顯示一張圖像的實(shí)現(xiàn),具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過來看看吧
    2020-04-04
  • Python批量實(shí)現(xiàn)Word/EXCEL/PPT轉(zhuǎn)PDF

    Python批量實(shí)現(xiàn)Word/EXCEL/PPT轉(zhuǎn)PDF

    在日常辦公和文檔處理中,有時(shí)我們需要將多個(gè)Word文檔、Excel表格或PPT演示文稿轉(zhuǎn)換為PDF文件,本文將介紹如何使用Python編程語言批量實(shí)現(xiàn)將多個(gè)Word、Excel和PPT文件轉(zhuǎn)換為PDF文件,需要的可以參考下
    2023-09-09
  • pytorch使用tensorboard報(bào)錯(cuò)問題及解決

    pytorch使用tensorboard報(bào)錯(cuò)問題及解決

    這篇文章主要介紹了pytorch使用tensorboard報(bào)錯(cuò)問題及解決方案,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2023-09-09
  • python利用蒙版摳圖(使用PIL.Image和cv2)輸出透明背景圖

    python利用蒙版摳圖(使用PIL.Image和cv2)輸出透明背景圖

    這篇文章主要介紹了python利用蒙版摳圖(使用PIL.Image和cv2)輸出透明背景圖,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2020-08-08
  • 一文深入了解Python中的繼承知識(shí)點(diǎn)

    一文深入了解Python中的繼承知識(shí)點(diǎn)

    Python?是面向?qū)ο蟮木幊陶Z言,因此支持面向?qū)ο蟮娜筇匦灾唬豪^承。本文就帶大家了解了解Python中繼承的相關(guān)知識(shí)點(diǎn),感興趣的可以了解一下
    2022-11-11
  • python heic后綴圖片文件轉(zhuǎn)換成jpg格式的操作

    python heic后綴圖片文件轉(zhuǎn)換成jpg格式的操作

    這篇文章主要介紹了python heic后綴圖片文件轉(zhuǎn)換成jpg格式的操作,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過來看看吧
    2021-03-03
  • 深入了解Python?Flask框架之藍(lán)圖

    深入了解Python?Flask框架之藍(lán)圖

    這篇文章主要為大家介紹了Python?Flask框架之藍(lán)圖,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下,希望能夠給你帶來幫助
    2021-12-12
  • Python編程使用有限狀態(tài)機(jī)識(shí)別地址有效性

    Python編程使用有限狀態(tài)機(jī)識(shí)別地址有效性

    這篇文章主要介紹了Python編程中如何使用有限狀態(tài)機(jī)識(shí)別地址有效性,如何識(shí)別一個(gè)地址是否有效,確切的講,如何編程識(shí)別一個(gè)中國地址是否有效
    2021-09-09
  • python使用KNN算法手寫體識(shí)別

    python使用KNN算法手寫體識(shí)別

    這篇文章主要為大家詳細(xì)介紹了python使用KNN算法手寫體識(shí)別,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2018-02-02
  • Python數(shù)據(jù)結(jié)構(gòu)之循環(huán)鏈表詳解

    Python數(shù)據(jù)結(jié)構(gòu)之循環(huán)鏈表詳解

    循環(huán)鏈表 (Circular Linked List) 是鏈?zhǔn)酱鎯?chǔ)結(jié)構(gòu)的另一種形式,它將鏈表中最后一個(gè)結(jié)點(diǎn)的指針指向鏈表的頭結(jié)點(diǎn),使整個(gè)鏈表頭尾相接形成一個(gè)環(huán)形,使鏈表的操作更加方便靈活。本文將詳細(xì)介紹一下循環(huán)鏈表的相關(guān)知識(shí),需要的可以參考一下
    2022-01-01

最新評(píng)論