您好,登錄后才能下訂單哦!
這篇文章主要介紹“Python協(xié)程的面試題有哪些”,在日常操作中,相信很多人在Python協(xié)程的面試題有哪些問(wèn)題上存在疑惑,小編查閱了各式資料,整理出簡(jiǎn)單好用的操作方法,希望對(duì)大家解答”Python協(xié)程的面試題有哪些”的疑惑有所幫助!接下來(lái),請(qǐng)跟著小編一起來(lái)學(xué)習(xí)吧!
一、什么是協(xié)程
協(xié)程:實(shí)現(xiàn)協(xié)作式多任務(wù),可以在程序執(zhí)行內(nèi)部中斷,轉(zhuǎn)而執(zhí)行其他協(xié)程。
比如我們編寫子程序(或者說(shuō)函數(shù)),通常是利用“調(diào)用”來(lái)實(shí)現(xiàn)從 A 跳去 B,B 跳去 C,如果想回來(lái)調(diào)用方,必須等被調(diào)用方執(zhí)行完才行,整個(gè)調(diào)用過(guò)程是通過(guò)棧實(shí)現(xiàn)的。而協(xié)程是運(yùn)行子程序的過(guò)程中“中斷”,轉(zhuǎn)而執(zhí)行其他子程序,再在適當(dāng)?shù)臅r(shí)候返回來(lái)接著運(yùn)行。
二、協(xié)程與線程的區(qū)別
協(xié)程相比于線程的優(yōu)勢(shì):
1、協(xié)程效率比線程高。線程間切換需要開(kāi)銷,而協(xié)程間切換是由程序自身控制的,不需要開(kāi)銷。
2、協(xié)程不需要多線程的鎖機(jī)制。協(xié)程是在一個(gè)線程內(nèi)進(jìn)行切換,所以不存在同時(shí)寫變量沖突,不需要給共享資源加鎖,只需要判斷狀態(tài)。
PS:如果想使用多CPU的話,可以使用進(jìn)程+協(xié)程。
三、協(xié)程的實(shí)現(xiàn)
協(xié)程是通過(guò)yield實(shí)現(xiàn)的,所以協(xié)程是生成器,可以通過(guò) next 調(diào)用。
def simple(a): print("----start----") r = yield a print('----r------' + str(r)) >>> my_simple = simple(5) >>> my_simple <generator object simple at 0x10f9242b0> >>> next(my_simple) ----start---- 5 >>> my_simple.send(8) ----r------8 Traceback (most recent call last): File "<stdin>", line 1, in <module> StopIteration
以上是一個(gè)簡(jiǎn)單的協(xié)程例子,可以看到,my_simple是一個(gè)生成器實(shí)例,需要使用next()方法或send(None)去預(yù)激協(xié)程,協(xié)程運(yùn)行到y(tǒng)ield的時(shí)候停止,當(dāng)使用send()方法給yield賦值時(shí),程序繼續(xù)往下運(yùn)行,并拋出StopIteration異常。
四、協(xié)程返回值
在python3.3版本后,協(xié)程可以有返回值。
def simple(a): print("----start----") r = yield a print('----r------' + str(r)) return r >>> my_simple = simple(5) >>> next(my_simple) ----start---- 5 >>> my_simple.send(8) ----r------8 Traceback (most recent call last): File "<stdin>", line 1, in <module> StopIteration: 8
可以看到,在程序運(yùn)行完后,異常對(duì)象StopIteration的value屬性保存著返回的值。
五、使用yield from獲取協(xié)程返回值
對(duì)于yield from來(lái)說(shuō),解釋器不僅會(huì)捕獲StopIteration異常,還會(huì)把value屬性的值作為yield from表達(dá)式的值。
yield from主要功能是打開(kāi)雙向通道,把最外層的調(diào)用方和最內(nèi)層的子生成器連接起來(lái)。
# 子生成器 def total_num(): total = 0 while True: num = yield if num == None: break total += num return total # 委派生成器 def send_num(result, key): while True: result[key] = yield from total_num() # 調(diào)用方 def main(data): result = {} for key, nums in data.items(): group = send_num(result, key) next(group) for num in nums: group.send(num) group.send(None) print(result) data = { 'nums1': [12, 34, 23, 4, 35, 34, 34, 55], 'nums2': [22, 44, 33, 24, 33, 24, 4, 15], 'nums3': [32, 54, 43, 41, 31, 44, 24, 25], 'nums4': [42, 64, 53, 43, 37, 74, 74, 35], 'nums5': [52, 74, 63, 46, 39, 84, 44, 45] } if __name__ == "__main__": main(data)
輸出結(jié)果如下:
{'nums1': 231, 'nums2': 199, 'nums3': 294, 'nums4': 422, 'nums5': 447}
委派生成器作為雙向管道把調(diào)用方和子生成器連接起來(lái),委派生成器在yield from表達(dá)式處暫停時(shí),調(diào)用方通過(guò)send()方法把數(shù)據(jù)傳給子生成器,子生成器再把產(chǎn)出值發(fā)送給調(diào)用方,子生成器返回后,會(huì)拋出StopIteration異常,并把返回值添加到異常的value屬性上,此時(shí)你異常生成器會(huì)恢復(fù),并獲取異常的value值作為yield from表達(dá)式的值。
委派生成器相當(dāng)于管道,所以可以把任意個(gè)委派生成器連起來(lái):委派生成器連接的子生成器是一個(gè)委派生成器,以此類推,直到遇到一個(gè)使用yield的生成器或可迭代對(duì)象。
六、總結(jié)
1、協(xié)程是用于控制程序中斷,它與函數(shù)調(diào)用不同
2、協(xié)程是在單線程里可處理多任務(wù),相比多線程節(jié)省了線程切換的開(kāi)銷
3、協(xié)程通過(guò) yield 關(guān)鍵字實(shí)現(xiàn),它也是一種生成器
4、協(xié)程調(diào)用方可通過(guò) send() 方法給被調(diào)用方發(fā)送值。協(xié)程的開(kāi)啟需要預(yù)激,預(yù)激方法是:send(None)或者next()
5、協(xié)程在 python3.3 之后是有返回值的,返回值會(huì)放在 StopIteration 異常的 value 里
6、yield from 的作用是在生成器里調(diào)用子生成器,可以優(yōu)化一個(gè)嵌套 for 循環(huán)等復(fù)雜代碼
7、協(xié)程有四種狀態(tài):GEN_CREATED(等待開(kāi)始執(zhí)行)、GEN_RUNNING(解釋器正在執(zhí)行)、GEN_SUSPENDED(在yield表達(dá)式處停止)、GEN_CLOSED(執(zhí)行結(jié)束)。通過(guò) inspect.getgeneratorstate(…) 函數(shù)可獲取
8、結(jié)束協(xié)程的兩種方式:generator.throw 或 generator.close
七、相關(guān)面試題
上面介紹了關(guān)于Python學(xué)習(xí)協(xié)程的概念,相對(duì)應(yīng)的這里提供幾個(gè)關(guān)于協(xié)程的面試題目。
理論結(jié)合“面試”
1、什么是進(jìn)程、線程、協(xié)程?
答案要點(diǎn):
a、進(jìn)程是資源分配,每個(gè)進(jìn)程擁有獨(dú)立的資源空間,因?yàn)檫M(jìn)程不共享資源,所以就涉及到進(jìn)程間通信的方式,常見(jiàn)的方式有:消息隊(duì)列、管道、信號(hào)量、socket套接字等。(這里會(huì)引申出幾個(gè)面試題:進(jìn)程間有通信方式有哪些?-> 使用過(guò)哪些消息隊(duì)列?)
b、線程(英語(yǔ):thread)是操作系統(tǒng)能夠進(jìn)行運(yùn)算調(diào)度的最小單位。它被包含在進(jìn)程之中,是進(jìn)程中的實(shí)際運(yùn)作單位。線程是在進(jìn)程下,所以同一進(jìn)程下的多個(gè)線程是能共享資源的。線程能共享的資源有:堆、全局變量、文件描述符和信號(hào)處理等,不共享的資源:棧、寄存器等(這里會(huì)引申出的面試題:多線程怎么實(shí)現(xiàn)?-> 多線程并發(fā)問(wèn)題 -> 多線程共享哪些資源)
c、協(xié)程是單線程下實(shí)現(xiàn)多任務(wù),它通過(guò) yield 關(guān)鍵字來(lái)實(shí)現(xiàn),能有效地減少多線程之間切換的開(kāi)銷。它是一種比線程更加輕量級(jí)的存在。正如一個(gè)進(jìn)程可以擁有多個(gè)線程一樣,一個(gè)線程也可以擁有多個(gè)協(xié)程。
2、協(xié)程有什么優(yōu)缺點(diǎn)?
答案要點(diǎn)
a、協(xié)程不是被操作系統(tǒng)內(nèi)核所管理,而完全是由程序所控制(也就是在用戶態(tài)執(zhí)行),性能得到了很大的提升,不會(huì)像線程切換那樣消耗資源。
b、缺點(diǎn):異步代碼,可能不那么容易理解和調(diào)度
3、下面代碼輸出結(jié)果是什么?
def test(): print("1"*30) yield "A" print("A"*30) yield "B" print("B"*30) t = test() # 1 print(next(t)) # 2 print(next(t)) # 3 print(next(t)) # 4
答案要點(diǎn):
這其實(shí)是屬于生成器的一個(gè)題目,輸出如下:
1:沒(méi)有任何輸出,它不會(huì)執(zhí)行 print(“1”*30),只會(huì)返回一個(gè)生成器
2:輸出 “111…111”(30個(gè)),同時(shí)打印返回值"A"
3:輸出 “111…111”(30個(gè)),同時(shí)打印返回值"B"
4:異常、StopIteration
4、請(qǐng)寫一個(gè)簡(jiǎn)單的協(xié)程示例 或 利用協(xié)程實(shí)現(xiàn)一個(gè) 生產(chǎn)者消費(fèi)者 模式
面試一般其實(shí)比較少說(shuō)讓面試者手寫一個(gè)協(xié)程代碼,不過(guò)之前確實(shí)有遇到過(guò)讓手寫一個(gè)利用協(xié)程實(shí)現(xiàn)生產(chǎn)者消費(fèi)者模式的。
import time def consumer(): r = '' while True: n = yield r if not n: return print('[CONSUMER] Consuming %s...' % n) time.sleep(1) r = '200 OK' def produce(c): c.next() n = 0 while n < 5: n = n + 1 print('[PRODUCER] Producing %s...' % n) r = c.send(n) print('[PRODUCER] Consumer return: %s' % r) # 終止協(xié)程 # 終止協(xié)程的方式:generator.throw 或者 generator.close c.close() if __name__=='__main__': c = consumer() produce(c)
到此,關(guān)于“Python協(xié)程的面試題有哪些”的學(xué)習(xí)就結(jié)束了,希望能夠解決大家的疑惑。理論與實(shí)踐的搭配能更好的幫助大家學(xué)習(xí),快去試試吧!若想繼續(xù)學(xué)習(xí)更多相關(guān)知識(shí),請(qǐng)繼續(xù)關(guān)注億速云網(wǎng)站,小編會(huì)繼續(xù)努力為大家?guī)?lái)更多實(shí)用的文章!
免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。