python函數(shù)式編程學(xué)習(xí)之yield表達(dá)式形式詳解
前言
yield的英文單詞意思是生產(chǎn),剛接觸Python的時(shí)候感到非常困惑,一直沒(méi)弄明白yield的用法。最近又重新學(xué)習(xí)了下,所以整理了下面這篇文章,供自己和大家學(xué)習(xí)參考,下面話不多說(shuō)了,來(lái)一起看看詳細(xì)的介紹吧。
先來(lái)看一個(gè)例子
def foo():
print("starting...")
while True:
res = yield
print("res:",res)
g = foo()
next(g)
在上面的例子里,因?yàn)閒oo函數(shù)中有yield關(guān)鍵字,所以foo()函數(shù)的執(zhí)行結(jié)果g是一個(gè)生成器,此時(shí)可以使用next(g)或者g.__next__()方法觸發(fā)生成器的執(zhí)行
程序的執(zhí)行結(jié)果為
starting...
使用next(g)觸發(fā)生成器的執(zhí)行時(shí),程序會(huì)按照正常的順序從上向下執(zhí)行,遇到y(tǒng)ield,程序就會(huì)暫停
并把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那一行,此時(shí)再次調(diào)用next(g),程序會(huì)從yield語(yǔ)句那一行繼續(xù)向下運(yùn)行
修改上面的代碼,多調(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
可以看到,程序確實(shí)按猜想的步驟運(yùn)行,但是上面的程序也有一個(gè)很明顯的缺點(diǎn):那就是上面的代碼沒(méi)有任何的實(shí)際意義:res的值永遠(yuǎn)為None
在實(shí)際的開(kāi)發(fā)中,使用yield表達(dá)式形式的目的是yield可以得到一個(gè)值,然后yield把這個(gè)值賦值給某個(gè)變量,這樣才有實(shí)際意義
那應(yīng)該怎么操作才能為res變量賦一個(gè)值呢??那就是調(diào)用生成器自身的send方法
send方法可以觸發(fā)一次生成器執(zhí)行,同時(shí)還可以把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
來(lái)分析一下上面的代碼的執(zhí)行過(guò)程 :
1.程序開(kāi)始執(zhí)行以后,因?yàn)閒oo函數(shù)中有yield關(guān)鍵字,所以foo函數(shù)并不會(huì)真的執(zhí)行,而是先得到一個(gè)生成器g.
2.直到調(diào)用next方法,foo函數(shù)正式開(kāi)始執(zhí)行,先執(zhí)行foo函數(shù)中的print方法,然后進(jìn)入while循環(huán)
3.程序遇到y(tǒng)ield關(guān)鍵字,程序暫停,此時(shí)next(g)語(yǔ)句執(zhí)行完成
4.程序執(zhí)行g(shù).send(5),程序會(huì)從yield關(guān)鍵字那一行繼續(xù)向下運(yùn)行,send會(huì)把5這個(gè)值傳遞給yield
5.yield接收到send方法傳遞過(guò)來(lái)的值,然后由yield賦值給res變量
6.由于send方法中包含next()方法,所以程序會(huì)繼續(xù)向下運(yùn)行執(zhí)行print方法,然后再次進(jìn)入while循環(huán)
7.程序執(zhí)行再次遇到y(tǒng)ield關(guān)鍵字,yield會(huì)返回后面的值,由于yield后面沒(méi)有接任何參數(shù),所以yield會(huì)返回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í)行過(guò)程如同上面的分析的執(zhí)行過(guò)程一樣運(yùn)行
在上面的例子里,如果調(diào)用send方法時(shí),傳遞的參數(shù)為None,得到的結(jié)果會(huì)是怎么樣的呢??
從上面的分析中,可以知道:
如果`g.send()`方法發(fā)送給yield關(guān)鍵字的參數(shù)為None,則yield關(guān)鍵字傳遞給res變量的值就為None
由于yield后面本來(lái)沒(méi)有接任何值,所以yield返回的值默認(rèn)也為None,所以程序執(zhí)行結(jié)果會(huì)得到兩個(gè)None
修改代碼,驗(yàn)證上面的猜想
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í),傳遞的參數(shù)為None,則程序執(zhí)行的結(jié)果將會(huì)是兩個(gè)None
使用yield表達(dá)式形式實(shí)現(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
# 階段一:遞歸找文件的絕對(duì)路徑,把文件的完事路徑發(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
# 階段二:收到文件的完整路徑,打開(kāi)文件獲取文件對(duì)象,把文件對(duì)象發(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
# 階段三:收到文件對(duì)象,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)
path1 = "/root" # 定義要搜索的路徑
path2 = "/etc" # 定義要搜索的路徑
g = get_file_path(opener(cat_file(grep(printer(), "root"))))
print(g)
g.send(path1)
g.send(path2)
總結(jié)
以上就是這篇文章的全部?jī)?nèi)容了,希望本文的內(nèi)容對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,如果有疑問(wèn)大家可以留言交流,謝謝大家對(duì)腳本之家的支持。
相關(guān)文章
python腳本監(jiān)控logstash進(jìn)程并郵件告警實(shí)例
這篇文章主要介紹了python腳本監(jiān)控logstash進(jìn)程并郵件告警實(shí)例,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2020-04-04
window7下的python2.7版本和python3.5版本的opencv-python安裝過(guò)程
這篇文章主要介紹了window7下的python2.7版本和python3.5版本的opencv-python安裝過(guò)程,非常不錯(cuò),具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2019-10-10
Python描述數(shù)據(jù)結(jié)構(gòu)學(xué)習(xí)之哈夫曼樹(shù)篇
這篇文章主要給大家介紹了關(guān)于Python描述數(shù)據(jù)結(jié)構(gòu)學(xué)習(xí)之哈夫曼樹(shù)篇的相關(guān)資料,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2020-09-09
利用python對(duì)excel中一列的時(shí)間數(shù)據(jù)更改格式操作
這篇文章主要介紹了利用python對(duì)excel中一列的時(shí)間數(shù)據(jù)更改格式操作,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2020-07-07
Django使用channels + websocket打造在線聊天室
本文將教你如何使用channels + websocket打造個(gè)在線聊天室。一共只有四步,你可以輕松上手并學(xué)會(huì)。項(xiàng)目中大部分代碼是基于channels的官方文檔的,加入了些自己的理解,以便新手學(xué)習(xí)使用。2021-05-05
python GUI庫(kù)圖形界面開(kāi)發(fā)之PyQt5信號(hào)與槽事件處理機(jī)制詳細(xì)介紹與實(shí)例解析
這篇文章主要介紹了python GUI庫(kù)圖形界面開(kāi)發(fā)之PyQt5信號(hào)與槽事件處理機(jī)制詳細(xì)介紹與實(shí)例解析,需要的朋友可以參考下2020-03-03

