python中的yield使用詳解

2021-09-13 01:21:07 字數 4723 閱讀 2653

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 做一些其它的事...