yield是python的乙個關鍵字,剛接觸python的時候對這個關鍵字一知半解,掌握之後才發現這關鍵字有大用,本文將對yield的使用方法好好梳理一番。
在python中,生成器是一種可迭代物件,但可迭代物件不一定是生成器。
例如,list就是乙個可迭代物件
>>> a = list(range(3))
>>> for i in a:
print(i)01
23
但是乙個list物件所有的值都是放在記憶體中的,如果資料量非常大的話,記憶體就有可能不夠用;這種情況下,就可以生成器,例如,python可以用「()」構建生成器物件:
>>> b = (x for x in range(3))
>>> for i in b:
print(i)01
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
0gen 1
1gen 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 "", line 1, in 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 "", line 1, in c.send(none)
stopiteration
生成器的結果讀取完後,會產生乙個stopiteration的異常
yield乙個常見的使用場景是通過yield來實現協程,已下面這個生產者消費者模型為例:
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 produce(c)
file ".\foobar.py", line 47, in produce
c.send(none)
stopiteration
在上面的例子中可以看到,yield表示式與send配合,可以起到交換資料的效果,
n = yield r
r = c.send(n)
另外乙個比較有意思的使用場景是在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使用方法
今天在看其他同事的程式設計客棧 時,發現乙個沒使用過的python關鍵字 yield 先問了一下同事,聽他說了幾句,有個模糊的印象,僅僅是模糊而已。於是自己去搜搜資料看。看了半天,逐漸清晰了。不過在工作機制以及應用上還是有點迷茫。嗯,先把初始接觸的印象記下來吧。yield 簡單說來就是乙個生成器 g...
Python中yield表示式的使用
關於yield這個表示式,只是在以前用的時候網上搜了幾篇文章看了一下,大概理解如 何去使用,但yield執行原理其實還是不大明白,今天在次總結一下 1.如果乙個函式中使用了yield,那麼這個函式就變成了乙個生成器。生成器是可以迭代的,但只可以讀取它一次,因為用的時候才生成。2.生成器能夠迭代是因為...
Python中yield表示式的使用
關於yield這個表示式,只是在以前用的時候網上搜了幾篇文章看了一下,大概理解如 何去使用,但yield執行原理其實還是不大明白,今天在次總結一下 coding utf 8 defyield test n for i in range n yield call i print i i 做一些其它的事...