這篇文章帶給大家的內容是關於python中的yield關鍵字的用法介紹(程式碼範例),有一定的參考價值,有需要的朋友可以參考一下,希望對你有幫助。
yield是python的一個關鍵字,剛接觸python的時候對這個關鍵字一知半解,掌握之後才發現這關鍵字有大用,本文將對yield的使用方法好好梳理一番。
1 使用yield創建生成器
在python中,生成器是一種可迭代對象,但可迭代對像不一定是生成器。
例如,list就是一個可迭代物件
>>> a = list(range(3)) >>> for i in a: print(i) 0 1 2 3
但是一個list物件所有的值都是放在記憶體中的,如果資料量非常大的話,記憶體就有可能不夠用;這種情況下,就可以生成器,例如,python可以用「()」建構生成器物件:
>>> b = (x for x in range(3)) >>> for i in b: print(i) 0 1 2 >>> for i in b: print(i) >>>
生成器可以迭代的,並且資料即時生成,不會全部保存在記憶體中;值得注意的是,產生器只能讀取一次,從上面的運行結果可以看到,第二次for迴圈輸出的結果為空。
在實際編程中,如果一個函數需要產生一段序列化的數據,最簡單的方法是將所有結果都放在一個list裡返回,如果數據量很大的話,應該考慮用生成器來改寫直接傳回列表的函數(Effective Python, Item 16).
>>> def get_generator(): for i in range(3): print('gen ', i) yield i >>> c = get_generator() >>> c = get_generator() >>> for i in c: print(i) gen 0 0 gen 1 1 gen 2 2
由上面的程式碼可以看出,當呼叫get_generator函數時,並不會執行函數內部的程式碼,而是傳回了一個迭代器對象,在用for迴圈進行迭代的時候,函數中的程式碼才會被執行。
除了使用for迴圈來獲得生成器回傳的值,還可以使用next和send
>>> c = get_generator() >>> print(next(c)) gen 0 0 >>> print(next(c)) gen 1 1 >>> print(next(c)) gen 2 2 >>> print(next(c)) Traceback (most recent call last): File "<pyshell#59>", line 1, in <module> print(next(c)) StopIteration
>>> c = get_generator() >>> c.send(None) gen 0 0 >>> c.send(None) gen 1 1 >>> c.send(None) gen 2 2 >>> c.send(None) Traceback (most recent call last): File "<pyshell#66>", line 1, in <module> c.send(None) StopIteration
產生器的結果讀取完後,會產生一個StopIteration的異常
#2 coroutines中使用
yield一個常見的使用場景是透過yield來實現協程,已下面這個生產者消費者模型為例:
# import logging # import contextlib # def foobar(): # logging.debug('Some debug data') # logging.error('Some error data') # logging.debug('More debug data') # @contextlib.contextmanager # def debug_logging(level): # logger = logging.getLogger() # old_level = logger.getEffectiveLevel() # logger.setLevel(level) # try: # yield # finally: # logger.setLevel(old_level) # with debug_logging(logging.DEBUG): # print('inside context') # foobar() # print('outside context') # foobar() def consumer(): r = 'yield' while True: print('[CONSUMER] r is %s...' % r) #当下边语句执行时,先执行yield r,然后consumer暂停,此时赋值运算还未进行 #等到producer调用send()时,send()的参数作为yield r表达式的值赋给等号左边 n = yield r #yield表达式可以接收send()发出的参数 if not n: return # 这里会raise一个StopIteration print('[CONSUMER] Consuming %s...' % n) r = '200 OK' def produce(c): c.send(None) n = 0 while n < 5: n = n + 1 print('[PRODUCER] Producing %s...' % n) r = c.send(n) #调用consumer生成器 print('[PRODUCER] Consumer return: %s' % r) c.send(None) c.close() c = consumer() produce(c)
[CONSUMER] r is yield... [PRODUCER] Producing 1... [CONSUMER] Consuming 1... [CONSUMER] r is 200 OK... [PRODUCER] Consumer return: 200 OK [PRODUCER] Producing 2... [CONSUMER] Consuming 2... [CONSUMER] r is 200 OK... [PRODUCER] Consumer return: 200 OK [PRODUCER] Producing 3... [CONSUMER] Consuming 3... [CONSUMER] r is 200 OK... [PRODUCER] Consumer return: 200 OK [PRODUCER] Producing 4... [CONSUMER] Consuming 4... [CONSUMER] r is 200 OK... [PRODUCER] Consumer return: 200 OK [PRODUCER] Producing 5... [CONSUMER] Consuming 5... [CONSUMER] r is 200 OK... [PRODUCER] Consumer return: 200 OK Traceback (most recent call last): File ".\foobar.py", line 51, in <module> produce(c) File ".\foobar.py", line 47, in produce c.send(None) StopIteration
在上面的範例中可以看到,yield表達式與send配合,可以起到交換資料的效果,
n = yield r r = c.send(n)
3 contextmanager中使用
另外一個比較有趣的使用場景是在contextmanager中,如下:
import logging import contextlib def foobar(): logging.debug('Some debug data') logging.error('Some error data') logging.debug('More debug data') @contextlib.contextmanager def debug_logging(level): logger = logging.getLogger() old_level = logger.getEffectiveLevel() logger.setLevel(level) try: yield #这里表示with块中的语句 finally: logger.setLevel(old_level) with debug_logging(logging.DEBUG): print('inside context') foobar() print('outside context') foobar()
inside context DEBUG:root:Some debug data ERROR:root:Some error data DEBUG:root:More debug data outside context ERROR:root:Some error data
在上面的程式碼中,透過使用上下文管理器(contextmanager)來臨時提升了日誌的等級,yield表示with區塊中的語句;
總結
yield表達式可以建立生成器,應該考慮使用生成器來改寫直接返回list的函數;
#由於生成器只能讀取一次,因此使用for迴圈遍歷的時候要格外注意;生成器讀取完後繼續讀的話會raise一個StopIteration的異常,實際編程中可以使用這個異常來作為讀取終止的判斷依據;
yield一個常見的使用場景是實現協程;透過與send函數的配合,可以起到交換資料的效果;
yield也可以在contextmanager修飾的函數中表示with區塊中的語句
以上是python中的yield關鍵字的用法介紹(程式碼範例)的詳細內容。更多資訊請關注PHP中文網其他相關文章!