要理解yield,必須先清楚可迭代對(duì)象藻丢、迭代器和生成器的概念犁跪。
一、可迭代對(duì)象
大部分對(duì)象都是可迭代法希,只要實(shí)現(xiàn)了__iter__
方法的對(duì)象(可以是自定義的容器)就是可迭代的,例如常見(jiàn)的list枷餐、tuple、str, set苫亦。
__iter__
方法會(huì)返回迭代器(iterator)本身毛肋。
判斷一個(gè)對(duì)象是否是可迭代對(duì)象的方法:
isinstance(Object, Iterable)
可迭代對(duì)象一般都用for循環(huán)遍歷元素,也就是能用for循環(huán)的對(duì)象都可稱為可迭代對(duì)象, 如何對(duì)非可迭代對(duì)象使用for會(huì)報(bào)錯(cuò)屋剑。
可迭代對(duì)象生成后, 所有的值都存在內(nèi)存當(dāng)中,并不適合大量數(shù)據(jù)润匙。
二、迭代器
具有next方法的對(duì)象都是迭代器唉匾。在調(diào)用next方法時(shí)孕讳,迭代器會(huì)返回它的下一個(gè)值匠楚。如果next方法被調(diào)用,但迭代器沒(méi)有值可以返回厂财,就會(huì)引發(fā)一個(gè)StopIteration異常,通過(guò)except 這個(gè)異秤蟛荆可以判斷迭代器遍歷完成。
使用迭代器的好處:
1)如果使用列表璃饱,計(jì)算值時(shí)會(huì)一次獲取所有值与斤,那么就會(huì)占用更多的內(nèi)存。而迭代器則是一個(gè)接一個(gè)計(jì)算荚恶。
2)使代碼更通用撩穿、更簡(jiǎn)單。
三谒撼、生成器
1)任何包含yield語(yǔ)句的函數(shù)都稱為生成器食寡。
2)生成器都是一個(gè)迭代器,但迭代器不一定是生成器嗤栓。
對(duì)于生成器, 只有你需要的時(shí)候它才會(huì)求值, 這也是和可迭代對(duì)象的區(qū)別冻河。
3 ) 一般生成器都是通過(guò)生成器推導(dǎo)式或者包含yield的函數(shù)聲明
----生成器推導(dǎo)式和組建:
變量L= (item或item表達(dá)式 for item in 列表/集合)
# 通過(guò)`yield`來(lái)創(chuàng)建生成器
def func():
for i in xrange(10);
yield i
# 通過(guò)列表來(lái)創(chuàng)建生成器
[i for i in xrange(10)]
4 ) 帶有 yield 的函數(shù)不再是一個(gè)普通函數(shù),而是一個(gè)生成器generator茉帅,可用于迭代叨叙。
yield的作用:
1.它和return差不多的用法,只是擁有它的語(yǔ)法結(jié)構(gòu)最后是返回了一個(gè)生成器堪澎。
2.了解yield 必須知道擂错,當(dāng)你調(diào)用yield所在的那個(gè)函數(shù)或者生成器表達(dá)式的時(shí)候,那個(gè)函數(shù)并沒(méi)有運(yùn)行樱蛤,只會(huì)返回一個(gè)生成器的對(duì)象钮呀。
3.當(dāng)你第一次在for中調(diào)用生成器的的對(duì)象,它將會(huì)運(yùn)行你函數(shù)中的代碼從最開始一直到到碰到了yield的關(guān)鍵字昨凡,然后它會(huì)返回循環(huán)中的第一個(gè)值爽醋。然后每一次其他的調(diào)用將會(huì)運(yùn)行你在這個(gè)函數(shù)中所寫的循環(huán)多一次(第二次循環(huán)從第一次返回的yield位置后面開始到遇到下一個(gè)yield
)调榄,并且返回下一個(gè)值朝蜘,直到?jīng)]有值可以返回了,此時(shí)迭代器遍歷完成。
與生成器(實(shí)際上是迭代器)相關(guān)的next()和send()方法:
生成器可以被for調(diào)用, 也可以使用send和next方法手動(dòng)進(jìn)行遍歷控制,實(shí)現(xiàn)更復(fù)雜的功能跨琳。
對(duì)于普通的生成器哪痰,第一個(gè)next調(diào)用遂赠,相當(dāng)于啟動(dòng)生成器,會(huì)從生成器函數(shù)的第一行代碼開始執(zhí)行晌杰,直到第一次執(zhí)行完yield語(yǔ)句
send(msg)與next()都有返回值跷睦,它們的返回值是當(dāng)前迭代遇到y(tǒng)ield時(shí),yield后面表達(dá)式的值肋演,其實(shí)就是當(dāng)前迭代循環(huán)()中yield后面的參數(shù)抑诸。
第一次調(diào)用時(shí)必須先next()或send(None)烂琴,否則會(huì)報(bào)錯(cuò),send后之所以為None是因?yàn)檫@時(shí)候沒(méi)有上一個(gè)yield(根據(jù)第8條)哼鬓〖嘤遥可以認(rèn)為边灭,next()等同于send(None)异希。
區(qū)別是:
send可以強(qiáng)行修改上一個(gè)yield表達(dá)式值, 多了一次賦值的動(dòng)作。
send語(yǔ)句伴隨著類似n1 = yield ret
的結(jié)構(gòu), 旨在從循環(huán)外傳入數(shù)據(jù)而影響循環(huán)绒瘦。
注意, 從第二次循環(huán)開始, send語(yǔ)句傳遞賦值到 n1 然后繼續(xù)執(zhí)行循環(huán)并遇到下一個(gè)yield称簿。
具體流程參考
python中yield控制的協(xié)程
協(xié)程是一種用戶態(tài)的輕量級(jí)線程,又稱微線程惰帽,英文名Coroutine憨降,本質(zhì)上還是一個(gè)線程
, 擁有線程的共享代碼段(代碼和常量),數(shù)據(jù)段(全局變量和靜態(tài)變量)该酗,擴(kuò)展段(堆存儲(chǔ))授药。協(xié)程的調(diào)度完全由用戶控制。人們通常將協(xié)程和子程序(函數(shù))比較著理解呜魄。
子程序調(diào)用總是一個(gè)入口悔叽,一次返回,一旦退出即完成了子程序的執(zhí)行爵嗅。
協(xié)程的起始處是第一個(gè)入口點(diǎn)娇澎,在協(xié)程里,返回點(diǎn)之后是接下來(lái)的入口點(diǎn)睹晒。在python中趟庄,協(xié)程可以通過(guò)yield來(lái)調(diào)用其它協(xié)程。通過(guò)yield方式轉(zhuǎn)移執(zhí)行權(quán)的協(xié)程之間不是調(diào)用者與被調(diào)用者的關(guān)系伪很,而是彼此對(duì)稱戚啥、平等的,通過(guò)相互協(xié)作共同完成任務(wù)锉试。其運(yùn)行的大致流程如下:
- 第一步猫十,協(xié)程A開始執(zhí)行。
- 第二步键痛,協(xié)程A執(zhí)行到一半炫彩,進(jìn)入暫停(這里是生成器完成一次生成),通過(guò)yield命令將執(zhí)行權(quán)轉(zhuǎn)移到協(xié)程B絮短。
- 第三步江兢,(一段時(shí)間后)協(xié)程B交還執(zhí)行權(quán)并傳遞信息給下一次生成循環(huán)(send方法)。
- 第四步丁频,協(xié)程A恢復(fù)執(zhí)行杉允。