python的yield和yield from
yield
為了理解什么是?yield,你必須理解什么是生成器。在理解生成器之前,讓我們先知道什么是迭代。
可迭代對象
當你建立了一個列表,你可以逐項地讀取這個列表,這叫做一個可迭代對象:
>>> mylist = [1, 2, 3] >>> for i in mylist : ... print(i) 1 2 3mylist?是一個可迭代的對象。當你使用一個列表生成式來建立一個列表的時候,就建立了一個可迭代的對象:
>>> mylist = [x*x for x in range(3)] >>> for i in mylist : ... print(i) 0 1 4使用迭代器會把所有的值都存儲到了內存中,如果你有大量數據的話這個方式并不是你想要的。?
生成器
生成器是可以迭代的,但是你?只可以讀取它一次?,因為它并不把所有的值放在內存中,它是實時地生成數據
def get_yield(limit):for i in range(limit):yield it = get_yield(5) print(t) # t輸出為 <generator object get_yield at 0x000001C69833CEB8> for num in t:print(num) # 下面這一段不會輸出 for num in t:print(num)yield和return很相似,都起到返回值的作用,但是yield所在的函數返回的是一個生成器,return則直接將某個變量返回,并退出函數。要知道,上面的get_yield函數并不是一被調用就馬上執行,可以觀看下段代碼來更好地了解這一點。
import time def oneByone(limit):for i in range(limit):print('oneByone里面i的值是:', i)yield it = oneByone(5) for j in range(5):i = next(t)print(print('main函數里面i的值是:' , i)time.sleep(1)print(time.strftime('%Y-%m-%d %H:%M:%S',time.localtime(time.time()))輸出結果:
oneByone里面i的值是: 0 main函數里面i的值是: 0 2018-07-30 12:40:54 oneByone里面i的值是: 1 main函數里面i的值是: 1 2018-07-30 12:40:55 oneByone里面i的值是: 2 main函數里面i的值是: 2 2018-07-30 12:40:56 oneByone里面i的值是: 3 main函數里面i的值是: 3 2018-07-30 12:40:57 oneByone里面i的值是: 4 main函數里面i的值是: 4 2018-07-30 12:40:58具體理解可以參見高級用法部分的next方法
高級用法:
1.控制生成器的窮盡(摘自該文章)
>>> class Bank(): # let's create a bank, building ATMs ... crisis = False ... def create_atm(self) : ... while not self.crisis : ... yield "$100" >>> hsbc = Bank() # when everything's ok the ATM gives you as much as you want >>> corner_street_atm = hsbc.create_atm() >>> print(next(corner_street_atm)) $100 >>> print(next(corner_street_atm)) $100 >>> print([next(corner_street_atm) for cash in range(5)]) ['$100', '$100', '$100', '$100', '$100'] >>> hsbc.crisis = True # crisis is coming, no more money! >>> print(next(corner_street_atm)) <type 'exceptions.StopIteration'> >>> wall_street_atm = hsbc.create_atm() # it's even true for new ATMs >>> print(next(wall_street_atm)) <type 'exceptions.StopIteration'> >>> hsbc.crisis = False # trouble is, even post-crisis the ATM remains empty >>> print(next(corner_street_atm)) <type 'exceptions.StopIteration'> >>> brand_new_atm = hsbc.create_atm() # build a new one to get back in business >>> for cash in brand_new_atm : ... print cash $100 $100 $100 $100 $100 $100 $100 $100 $100 ...這里面的邏輯是這么一回事:首先實現了一個Bank類,然后定義了類的所有實例共享的成員變量crisis,當crisis為False時,生成器create_atm()可迭代,反之不行。創建一個hsbc實例,首先創建corner_street_atm這個生成器,可以一直使用next(corner_street_atm)來迭代,但是一旦將crisis設為True,且再次調用next(corner_street_atm)時,該生成器就不可迭代了
(注意,將crisis設置為True之后,一定要再次調用生成器該生成器才會不可迭代
這里的邏輯其實很好懂,為什么需要再次調用才會不可迭代呢?因為上面的函數并不會一次性執行完,而是使用next則執行一次,所以當你將crisis設置為True時,如果不使用next方法,則一直不會跳出while循環)。
?
2.send和next方法
python生成器有兩個主要方法,一個是send一個是next。
在每段代碼中,第一個next調用,相當于啟動生成器,會從生成器函數的第一行代碼開始執行,直到第一次執行完yield語句(第4行)后,跳出生成器函數。然后第二個next調用,進入生成器函數后,從yield語句的下一句語句(第5行)開始執行,然后重新運行到yield語句,執行后,跳出生成器函數后面再次調用next,依此類推。即執行一次next則調用一次生成器函數,直至拋出不可迭代的錯誤。
1 def consumer(): 2 r = 0 3 for i in xrange(3): 4 yield r 5 r = '200 OK'+ str(i) 6 7 c = consumer() 8 n1 = c.next() 9 n2 = c.next() 10 n3 = c.next()(該段代碼摘自該文章)
send方法和next方法其實很相似,唯一的區別在于send方法可以傳入值,而next方法不能傳值,可以這樣說,next(c) = c.send(None)
第一次調用時不能使用send發送一個非None的值,否則會出錯的,因為沒有Python yield語句來接收這個值。
def consumer():r = ''while True:n = yield r # Aif not n:returnprint('[CONSUMER] Consuming %s...' % n)r = '200 OK'def produce(c):c.send(None) # B n = 0while n < 5:n = n + 1print('[PRODUCER] Producing %s...' % n)r = c.send(n) # Cprint('[PRODUCER] Consumer return: %s' % r)c.close()c = consumer() produce(c)運行結果:
[PRODUCER] Producing 1... [CONSUMER] Consuming 1... [PRODUCER] Consumer return: 200 OK [PRODUCER] Producing 2... [CONSUMER] Consuming 2... [PRODUCER] Consumer return: 200 OK [PRODUCER] Producing 3... [CONSUMER] Consuming 3... [PRODUCER] Consumer return: 200 OK [PRODUCER] Producing 4... [CONSUMER] Consuming 4... [PRODUCER] Consumer return: 200 OK [PRODUCER] Producing 5... [CONSUMER] Consuming 5... [PRODUCER] Consumer return: 200 OK在該段代碼里面,首先是創建了生成器c,然后將c傳入produce里面。下面開始講解produce和consume函數的交互:
首先執行c.send(None),然后進入生成器函數里面,執行到A處(注意,此處沒有執行賦值操作,遇見yield就跳回B了),然后從B的末尾繼續執行,在C處執行r = c.send(1),然后又跳回到生成器函數的A處,那么send是怎么賦值的呢???這里是將(yield r)當做一個整體,然后將該值賦給n,也就是n=1了,繼續執行A后面的語句,直至再次到A處,重復前面的步驟至循環結束。
另外,C處的語句r都會接受到生成器函數返回的r值。
3.yield from
https://www.cnblogs.com/cdma/p/6839640.html
https://blog.csdn.net/chenbin520/article/details/78111399?locationNum=7&fps=1
https://www.liaoxuefeng.com/wiki/0014316089557264a6b348958f449949df42a6d3a2e542c000/001432090954004980bd351f2cd4cc18c9e6c06d855c498000
總結
以上是生活随笔為你收集整理的python的yield和yield from的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: matplotlib各个部分
- 下一篇: 如何写第一个scrapy