您好,登錄后才能下訂單哦!
前言
yield的英文單詞意思是生產(chǎn),剛接觸Python的時候感到非常困惑,一直沒弄明白yield的用法。最近又重新學(xué)習(xí)了下,所以整理了下面這篇文章,供自己和大家學(xué)習(xí)參考,下面話不多說了,來一起看看詳細(xì)的介紹吧。
先來看一個例子
def foo(): print("starting...") while True: res = yield print("res:",res) g = foo() next(g)
在上面的例子里,因為foo函數(shù)中有yield關(guān)鍵字,所以foo()函數(shù)的執(zhí)行結(jié)果g是一個生成器,此時可以使用next(g)或者g.__next__()方法觸發(fā)生成器的執(zhí)行
程序的執(zhí)行結(jié)果為
starting...
使用next(g)觸發(fā)生成器的執(zhí)行時,程序會按照正常的順序從上向下執(zhí)行,遇到y(tǒng)ield,程序就會暫停
并把yield后面所接的值返回
打印next(g)的執(zhí)行結(jié)果
def foo(): print("starting...") while True: res = yield print("res:",res) g = foo() print(next(g))
程序執(zhí)行結(jié)果
starting... None
在上面的例子里,執(zhí)行一次next(g)方法,程序暫停在yield那一行,此時再次調(diào)用next(g),程序會從yield語句那一行繼續(xù)向下運行
修改上面的代碼,多調(diào)用幾次next方法,并打印next方法的返回結(jié)果
def foo(): print("starting...") while True: res = yield print("res:",res) g = foo() print(next(g)) print("*"*20) print(next(g))
上面這段代碼的執(zhí)行結(jié)果為
starting... None ******************** res: None None
可以看到,程序確實按猜想的步驟運行,但是上面的程序也有一個很明顯的缺點:那就是上面的代碼沒有任何的實際意義:res的值永遠(yuǎn)為None
在實際的開發(fā)中,使用yield表達式形式的目的是yield可以得到一個值,然后yield把這個值賦值給某個變量,這樣才有實際意義
那應(yīng)該怎么操作才能為res變量賦一個值呢??那就是調(diào)用生成器自身的send方法
send方法可以觸發(fā)一次生成器執(zhí)行,同時還可以把send方法的參數(shù)傳遞給yield
修改上面的代碼
def foo(): print("starting...") while True: res = yield print("res:",res) g = foo() next(g) print(g.send(5))
程序的執(zhí)行結(jié)果為:
starting... res: 5 None
來分析一下上面的代碼的執(zhí)行過程 :
1.程序開始執(zhí)行以后,因為foo函數(shù)中有yield關(guān)鍵字,所以foo函數(shù)并不會真的執(zhí)行,而是先得到一個生成器g.
2.直到調(diào)用next方法,foo函數(shù)正式開始執(zhí)行,先執(zhí)行foo函數(shù)中的print方法,然后進入while循環(huán)
3.程序遇到y(tǒng)ield關(guān)鍵字,程序暫停,此時next(g)語句執(zhí)行完成
4.程序執(zhí)行g(shù).send(5),程序會從yield關(guān)鍵字那一行繼續(xù)向下運行,send會把5這個值傳遞給yield
5.yield接收到send方法傳遞過來的值,然后由yield賦值給res變量
6.由于send方法中包含next()方法,所以程序會繼續(xù)向下運行執(zhí)行print方法,然后再次進入while循環(huán)
7.程序執(zhí)行再次遇到y(tǒng)ield關(guān)鍵字,yield會返回后面的值,由于yield后面沒有接任何參數(shù),所以yield會返回None,程序再次暫停,直到再次調(diào)用next方法或send方法
修改代碼,多次調(diào)用send方法
def foo(): print("starting...") while True: res = yield print("res:",res) g = foo() next(g) print(g.send(5)) print("*"*20) print(g.send(10)) print("#"*20) print(g.send(15))
執(zhí)行程序,得到如下結(jié)果
starting... res: 5 None ******************** res: 10 None #################### res: 15 None
可以看到,上面代碼的執(zhí)行過程如同上面的分析的執(zhí)行過程一樣運行
在上面的例子里,如果調(diào)用send方法時,傳遞的參數(shù)為None,得到的結(jié)果會是怎么樣的呢??
從上面的分析中,可以知道:
如果`g.send()`方法發(fā)送給yield關(guān)鍵字的參數(shù)為None,則yield關(guān)鍵字傳遞給res變量的值就為None
由于yield后面本來沒有接任何值,所以yield返回的值默認(rèn)也為None,所以程序執(zhí)行結(jié)果會得到兩個None
修改代碼,驗證上面的猜想
def foo(): print("starting...") while True: res = yield print("res:",res) g = foo() next(g) print("#"*20) print(g.send(None))
查看程序的執(zhí)行結(jié)果
starting... #################### res: None None
從程序的執(zhí)行結(jié)果可以看出,如果調(diào)用生成器的send方法時,傳遞的參數(shù)為None,則程序執(zhí)行的結(jié)果將會是兩個None
使用yield表達式形式實現(xiàn)linux系統(tǒng)中的"grep -rl root /etc"命令
代碼如下:
import os def init(func): def wrapper(*args, **kwargs): g = func(*args, **kwargs) next(g) return g return wrapper @init def get_file_path(target): """ get file abspath # 階段一:遞歸找文件的絕對路徑,把文件的完事路徑發(fā)送給階段二 :param target: :return: """ while True: start_path = yield g = os.walk(start_path) for parent_dir, _, files in g: for file in files: file_path = r"%s\%s" % (parent_dir, file) target.send(file_path) @init def opener(target): """ get file obj # 階段二:收到文件的完整路徑,打開文件獲取文件對象,把文件對象發(fā)送給階段三 :param target: :return: """ while True: file_path = yield with open(file_path, encoding='utf-8') as f: target.send((file_path, f)) @init def cat_file(target): """ read file content # 階段三:收到文件對象,for循環(huán)讀取文件的每一行內(nèi)容,把每一行內(nèi)容發(fā)給階段四 :param target: :return: """ while True: file_path, f = yield for line in f: file_content = target.send((file_path, line)) if file_content: break @init def grep(target, pattern): """ grep function # 階段四:收到文件的一行內(nèi)容,判斷要查找的內(nèi)容是否在這一行中,如果在,則把文件名發(fā)送給階段五 :param target: :param pattern: :return: """ tag = False while True: file_path, line = yield tag tag = False if pattern in line: target.send(file_path) tag = True @init def printer(): """ print file name # 階段五:收到文件名,打印結(jié)果 :return: """ while True: filename = yield print(filename) path2 = "/root" # 定義要搜索的路徑 path3 = "/etc" # 定義要搜索的路徑 g = get_file_path(opener(cat_file(grep(printer(), "root")))) print(g) g.send(path2) g.send(path3)
總結(jié)
以上就是這篇文章的全部內(nèi)容了,希望本文的內(nèi)容對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,如果有疑問大家可以留言交流,謝謝大家對億速云的支持。
免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進行舉報,并提供相關(guān)證據(jù),一經(jīng)查實,將立刻刪除涉嫌侵權(quán)內(nèi)容。