摘要:新語法表達式語句可以被用在賦值表達式的右側(cè)在這種情況下,它就是表達式。表達式必須始終用括號括起來,除非它是作為頂級表達式而出現(xiàn)在賦值表達式的右側(cè)。
PEP原文 : https://www.python.org/dev/pe...
PEP標題: Coroutines via Enhanced Generators
PEP作者: Guido van Rossum, Phillip J. Eby
創(chuàng)建日期: 2005-05-10
合入版本: 2.5
譯者 :豌豆花下貓(Python貓 公眾號作者)
目錄簡介
動機
規(guī)格摘要
規(guī)格:將值發(fā)送到生成器
新的生成器方法:send(value)
新的語法:yield 表達式
規(guī)格:異常和清理
新語法:yield 允許在try-finally中
新的生成器方法:throw(type,value = None,traceback = None)
新的標準異常:GeneratorExit
新的生成器方法:close()
新的生成器方法:__del__()
可選的擴展
擴展的 continue 表達式
未決問題
示例
參考實現(xiàn)
致謝
參考文獻
版權(quán)
簡介這個 PEP 在生成器的 API 和語法方面,提出了一些增強功能,使得它們可以作為簡單的協(xié)程使用。這基本上是將下述兩個 PEP 的想法結(jié)合起來,如果它被采納,那它們就是多余的了:
PEP-288,關(guān)于生成器的屬性特征與異常(Attributes and Exceptions)。當前 PEP 沿用了它的下半部分,即生成器的異常(事實上,throw() 的方法名就取自 PEP-288)。PEP-342 用 yield 表達式(這個概念來自 PEP-288 的早期版本)來替換了生成器的屬性特征。
PEP-325,生成器支持釋放資源。PEP-342 收緊了 PEP-325 中的一些松散的規(guī)范,使其更適用于實際的實現(xiàn)。
(譯注:PEP-288 和 PEP-325 都沒有被采納通過,它們的核心內(nèi)容被集成到了 PEP-342里。)
動機協(xié)程是表達許多算法的自然方式,例如模擬/仿真、游戲、異步 I/O、以及其它事件驅(qū)動編程或協(xié)同的多任務(wù)處理。Python 的生成器函數(shù)幾乎就是協(xié)程——但不完全是——因為它們允許暫停來生成值,但又不允許在程序恢復(fù)時傳入值或異常。它們也不允許在 try-finally 結(jié)構(gòu)的 try 部分作暫停,因此很難令一個異常退出的(aborted)協(xié)程來清理自己。
同樣地,當其它函數(shù)在執(zhí)行時,生成器不能提供控制,除非這些函數(shù)本身是生成器,并且外部生成器之所以寫了去 yield,是要為了響應(yīng)內(nèi)部生成器所 yield 的值。這使得即使是相對簡單的實現(xiàn)(如異步通信)也變得復(fù)雜,因為調(diào)用任意函數(shù),要么需要生成器變堵塞(block,即無法提供控制),要么必須在每個要調(diào)用的函數(shù)的周圍,添加一大堆引用循環(huán)代碼(a lot of boilerplate looping code)。
但是,如果有可能在生成器掛起的點上傳遞進來值或者異常,那么,一個簡單的協(xié)程調(diào)度器或蹦床函數(shù)(trampoline function)就能使協(xié)程相互調(diào)用且不用阻塞——對異步應(yīng)用程序有巨大好處。這些應(yīng)用程序可以編寫協(xié)程來運行非阻塞的 socket I/O,通過給 I/O 調(diào)度器提供控制,直到數(shù)據(jù)被發(fā)送或變?yōu)榭捎?。同時,執(zhí)行 I/O 的代碼只需像如下方式操作,就能暫停執(zhí)行,直到 nonblocking_read() 繼續(xù)產(chǎn)生一個值:
data = (yield nonblocking_read(my_socket, nbytes))
換句話說, 通過給語言和生成器類型增加一些相對較小的增強,Python 不需要為整個程序編寫一系列回調(diào),就能支持異步操作,并且對于本該需要數(shù)百上千個協(xié)作式的多任務(wù)偽線程的(co-operatively multitasking pseudothreads)程序,也可以不需要使用資源密集型線程。因此,這些增強功能將給標準 Python 帶來 Stackless Python 的許多優(yōu)點,又無需對 CPython 核心及其 API 進行任何重大的修改。此外,這些增強在任何已經(jīng)支持生成器的 Python 實現(xiàn)(例如 Jython)上都是可落實的。
規(guī)格摘要通過給生成器類型增加一些簡單的方法,以及兩個微小的語法調(diào)整,Python 開發(fā)者就能夠使用生成器函數(shù)來實現(xiàn)協(xié)程與其它的協(xié)作式多任務(wù)。這些方法和調(diào)整是:
重定義 yield 為表達式(expression),而不是語句(statement)。當前的 yield 語句將變成一個 yield 表達式,其值將被丟棄。每當通過正常的 next() 調(diào)用來恢復(fù)生成器時,yield 表達式的返回值是 None。
為生成器(generator-iterator)添加一個新的 send() 方法,它會恢復(fù)生成器,并且 send 一個值作為當前表達式的結(jié)果。send() 方法返回的是生成器產(chǎn)生的 next 值,若生成器沒有產(chǎn)生值就退出的話,則拋出 StopIteration 。
為生成器(generator-iterator)添加一個新的 throw() 方法,它在生成器暫停處拋出異常,并返回生成器產(chǎn)生的下一個值,若生成器沒有產(chǎn)生值就退出的話,則拋出 StopIteration (如果生成器沒有捕獲傳入的異常,或者它引發(fā)了其它異常,則該異常會傳遞給調(diào)用者。)
為生成器(generator-iterator)添加一個新的 close() 方法,它在生成器暫停處引發(fā) GeneratorExit 。如果生成器在之后引發(fā) StopIteration (通過正常退出,或者已經(jīng)被關(guān)閉)或 GeneratorExit (通過不捕獲異常),則 close() 返回給其調(diào)用者。如果生成器產(chǎn)生一個值,則拋出 RuntimeError。如果生成器引發(fā)任何其它異常,也會傳遞給調(diào)用者。如果生成器已經(jīng)退出(異常退出或正常退出),則 close() 不執(zhí)行任何操作。
增加了支持,確保即使在生成器被垃圾回收時,也會調(diào)用 close()。
允許 yield 在 try-finally 塊中使用,因為現(xiàn)在允許在 finally 語句中執(zhí)行垃圾回收或顯式地調(diào)用 close() 。
實現(xiàn)了所有這些變更的原型補丁已經(jīng)可用了,可作為當前 Python CVS HEAD 的 SourceForge 補丁。# 1223381
設(shè)計規(guī)格:將值發(fā)送進生成器 新的生成器方法:send(value)為生成器提出了一種新的方法,即 send() 。它只接收一個參數(shù),并將它發(fā)送給生成器。調(diào)用 send(None) 完全等同于調(diào)用生成器的 next() 方法。使用其它參數(shù)調(diào)用 send() 也有同樣的效果,不同的是,當前生成器表達式產(chǎn)生的值會不一樣。
因為生成器在生成器函數(shù)體的頭部執(zhí)行,所以在剛剛創(chuàng)建生成器時不會有 yield 表達式來接收值,因此,當生成器剛啟動時,禁止使用非 None 參數(shù)來調(diào)用 send() ,如果調(diào)用了,就會拋出 TypeError (可能是由于某種邏輯錯誤)。所以,在與協(xié)程通信前,必須先調(diào)用 next() 或 send(None) ,來將程序推進到第一個 yield 表達式。
與 next() 方法一樣,send() 方法也返回生成器產(chǎn)生的下一個值,或者拋出 StopIteration 異常(當生成器正常退出,或早已退出時)。如果生成器出現(xiàn)未捕獲的異常,則它會傳給調(diào)用者。
新語法:yield 表達式yield 語句(yield-statement)可以被用在賦值表達式的右側(cè);在這種情況下,它就是 yield 表達式(yield-expression)。除非使用非 None 參數(shù)調(diào)用 send() ,否則 yield 表達式的值就是 None。見下文。
yield 表達式必須始終用括號括起來,除非它是作為頂級表達式而出現(xiàn)在賦值表達式的右側(cè)。所以,下面例子都是合法的:
x = yield 42 x = yield x = 12 + (yield 42) x = 12 + (yield) foo(yield 42) foo(yield)
而下面的例子則是非法的(舉了一些特例的原因是,當前的 yield 12,42 是合法的):
x = 12 + yield 42 x = 12 + yield foo(yield 42, 12) foo(yield, 12)
請注意,如今沒有表達式的 yield-語句 和 yield-表達式是合法的。這意味著:當 next() 調(diào)用中的信息流被反轉(zhuǎn)時,應(yīng)該可以在不傳遞顯式的值的情況下 yield (yield 當然就等同于 yield None)。
當調(diào)用 send(value) 時,它恢復(fù)的 yield 表達式將返回傳入的值。當調(diào)用 next() 時,它恢復(fù)的 yield 表達式將返回 None。如果 yield-表達式(yield-expression)是一個 yield-語句(yield-statement),其返回值會被忽略,就類似于忽略用作語句的函數(shù)的返回值。
實際上,yield 表達式就像一個反函數(shù)調(diào)用(inverted function);它所 yield 的值實際上是當前函數(shù)返回(生成)的,而它 return 的值則是通過 send() 傳入的參數(shù)。
提示:這樣的拓展語法,使得它非常地接近于 Ruby。這是故意的。請注意,Python 在阻塞時,通過使用 send(EXPR) 而不是 return EXPR 來傳值給生成器,并且在生成器與阻塞之間傳遞控制權(quán)的底層機制完全不同。Python 中的阻塞不會被編譯成 thunk,相反,yield 暫停生成器的執(zhí)行進度。有一些不是這樣的特例,在 Python 中,你不能保存阻塞以供后續(xù)調(diào)用,并且你無法測試是否存在著阻塞。(XXX - 關(guān)于阻塞的這些東西似乎不合適,或許 Guido 會編輯下,做澄清。)
設(shè)計規(guī)格:異常和清理讓生成器對象成為通過調(diào)用生成器函數(shù)而生成的迭代器。本節(jié)中的 g 指的都是生成器對象。
新語法:yield 允許在 try-finally 里生成器函數(shù)的語法被拓展了,允許在 try-finally 語句中使用 yield 語句。
新的生成器方法:throw(type,value = None,traceback = None)g.throw(type, value, traceback) 會使生成器在掛起的點處拋出指定的異常(即在 yield 語句中,或在其函數(shù)體的頭部、且還未調(diào)用 next() 時)。如果生成器捕獲了異常,并生成了新的值,則它就是 g.throw() 的返回值。如果生成器沒有捕獲異常,那 throw() 也會拋出同樣的異常(它溜走了)。如果生成器拋出其它異常(包括返回時產(chǎn)生的 StopIteration),那該異常會被 throw() 拋出??傊?,throw() 的行為類似于 next() 或 send(),除了它是在掛起點處拋出異常。如果生成器已經(jīng)處于關(guān)閉狀態(tài),throw() 只會拋出經(jīng)過它的異常,而不去執(zhí)行生成器的任何代碼。
拋出異常的效果完全像它所聲明的那樣:
raise type, value, traceback
會在暫停點執(zhí)行。type 參數(shù)不能是 None,且 type 與 value 的類型必須得兼容。如果 value 不是 type 的實例(instance),則按照 raise 語句創(chuàng)建異常實例的規(guī)則,用 value 來生成新的異常實例。如果提供了 traceback 參數(shù),則它必須是有效的 Python 堆棧(traceback)對象,否則會拋出 TypeError 。
注釋:選擇 throw() 這個名稱,有幾個原因。Raise 是一個關(guān)鍵字,因此不能作為方法的名稱。與 raise 不同(它在執(zhí)行點處即時地拋出異常),throw() 首先恢復(fù)生成器,然后才拋出異常。單詞 throw 意味著將異常拋在別處,并且跟其它語言相關(guān)聯(lián)。
考慮了幾個替代的方法名:resolve(), signal(), genraise(), raiseinto() 和 flush() 。沒有一個像 throw() 那般合適。
新的標準異常:GeneratorExit定義了一個新的標準異常 GeneratorExit,繼承自 Exception。生成器應(yīng)該繼續(xù)拋出它(或者就不捕獲它),或者通過拋出 StopIteration 來處理這個問題。
新的生成器方法:close()g.close() 由以下偽代碼定義:
def close(self): try: self.throw(GeneratorExit) except (GeneratorExit, StopIteration): pass else: raise RuntimeError("generator ignored GeneratorExit") # Other exceptions are not caught新的生成器方法:__del__()
g.__ del __() 是 g.close() 的裝飾器。當生成器對象被作垃圾回收時,會調(diào)用它(在 CPython 中,則是它的引用計數(shù)變?yōu)榱銜r)。如果 close() 引發(fā)異常, 異常的堆棧信息(traceback)會被打印到 sys.stderr 并被忽略掉;它不會退回到觸發(fā)垃圾回收的地方。這與類實例在處理 __del__()的異常時的方法一樣。
如果生成器對象被循環(huán)引用,則可能不會調(diào)用 g.__del__() 。這是當前 CPython 的垃圾收集器的表現(xiàn)。做此限制的原因是,GC 代碼需要在一個任意點打破循環(huán),以便回收它,在此之后,不允許 Python 代碼“看到”形成循環(huán)的對象,因為它們可能處于無效的狀態(tài)。被用于解開(hanging off)循環(huán)的對象不受此限制。
盡管實際上不太可能看到生成器被循環(huán)引用。但是,若將生成器對象存儲在全局變量中,則會通過生成器框架的 f_globals 指針創(chuàng)建一個循環(huán)。另外,若在數(shù)據(jù)結(jié)構(gòu)中存儲對生成器對象的引用,且該數(shù)據(jù)結(jié)構(gòu)被作為參數(shù)傳遞給生成器,這也會創(chuàng)造一個循環(huán)引用(例如,如果一個對象具有一個作為生成器的方法,并持有由該方法創(chuàng)建的運行中的迭代器的引用)。鑒于生成器的典型用法,這些情況都不太可能。
此外,CPython 在實現(xiàn)當前 PEP 時,每當由于錯誤或正常退出而終止執(zhí)行時,會釋放被生成器使用的框架對象(frame object)。這保證了那些無法被恢復(fù)的生成器不會成為無法回收的循環(huán)引用的部分。這就允許了其它代碼在 try-finally 或 with 語句中使用 close() (參考 PEP-343),確保了給定的生成器會正確地完結(jié)。
可選擴展 擴展的 continue 語句本 PEP 的早期草案提出了一種新的 continue EXPR 語法,用于 for 循環(huán)(繼承自 PEP-340),將 EXPR 的值傳給被遍歷的迭代器。此功能暫時被撤銷了,因為本 PEP 的范圍已經(jīng)縮小,只關(guān)注將值傳給生成器迭代器(generator-iterator),而非其它類型的迭代器。Python-Dev 郵件列表中的一些人也覺得為這個特定功能添加新語法是為時過早(would be premature at best)。
未決問題Python-Dev 郵件的討論提出了一些未決的問題。我羅列于此,附上我推薦的解決方案與它的動機。目前編寫的 PEP 也反映了這種喜好的解決方案。
當生成器產(chǎn)生另一個值作為對“GeneratorExit”異常的響應(yīng)時,close()應(yīng)該引發(fā)什么異常?
我最初選擇了 TypeError ,因為它表示生成器函數(shù)發(fā)生了嚴重的錯誤行為,應(yīng)該通過修改代碼來修復(fù)。但是 PEP-343 中的 with_template 裝飾器類使用了 RuntimeError 來進行類似處理??梢哉f它們都應(yīng)該使用相同的異常。我寧愿不為此目的引入新的異常類,因為它不是我希望人們捕獲的異常:我希望它變成一個 traceback 給程序員看到,然后進行修復(fù)。所以我覺得它們都應(yīng)該拋出 RuntimeError 。有一些先例:在檢測到無限遞歸的情況下,或者檢測到未初始化的對象(由于各種各樣的原因),核心 Python 代碼會拋出該異常。
Oren Tirosh 建議將 send() 方法重命名為 feed() ,以便能跟 consumer 接口兼容(規(guī)范參見:http://effbot.org/zone/consumer.htm)。
然而,仔細觀察 consumer 接口,似乎 feed() 所需的語義與 send() 不同,因為后者不能在剛啟動的生成器上作有意義的調(diào)用。此外,當前定義的 consumer 接口不包含對 StopIteration 的處理。
因此,創(chuàng)建一個貼合 consumer 接口的簡單的裝飾器,來裝飾生成器函數(shù),似乎會更有用。舉個例子,它可以用初始的 next() 調(diào)用給生成器預(yù)熱(warm up),追蹤 StopIteration,甚至可以通過重新調(diào)用生成器來提供 reset() 用途。
示例一個簡單的 consumer 裝飾器,它使生成器函數(shù)在最初調(diào)用時,就自動地前進到第一個 yield 點:
def consumer(func): def wrapper(*args,**kw): gen = func(*args, **kw) gen.next() return gen wrapper.__name__ = func.__name__ wrapper.__dict__ = func.__dict__ wrapper.__doc__ = func.__doc__ return wrapper
一個使用 consumer 裝飾器創(chuàng)建反向生成器(reverse generator)的示例,該生成器接收圖像并創(chuàng)建縮略圖,再發(fā)送給其它 consumer。像這樣的函數(shù)可以鏈接在一起,形成 consumer 間的高效處理流水線,且每個流水線都可以具有復(fù)雜的內(nèi)部狀態(tài):
@consumer def thumbnail_pager(pagesize, thumbsize, destination): while True: page = new_image(pagesize) rows, columns = pagesize / thumbsize pending = False try: for row in xrange(rows): for column in xrange(columns): thumb = create_thumbnail((yield), thumbsize) page.write( thumb, col*thumbsize.x, row*thumbsize.y ) pending = True except GeneratorExit: # close() was called, so flush any pending output if pending: destination.send(page) # then close the downstream consumer, and exit destination.close() return else: # we finished a page full of thumbnails, so send it # downstream and keep on looping destination.send(page) @consumer def jpeg_writer(dirname): fileno = 1 while True: filename = os.path.join(dirname,"page%04d.jpg" % fileno) write_jpeg((yield), filename) fileno += 1 # Put them together to make a function that makes thumbnail # pages from a list of images and other parameters. # def write_thumbnails(pagesize, thumbsize, images, output_dir): pipeline = thumbnail_pager( pagesize, thumbsize, jpeg_writer(output_dir) ) for image in images: pipeline.send(image) pipeline.close()
一個簡單的協(xié)程調(diào)度器或蹦床(trampoline),它允許協(xié)程通過 yield 其它協(xié)程,來調(diào)用后者。被調(diào)用的協(xié)程所產(chǎn)生的非生成器的值,會被返回給調(diào)用方的協(xié)程。類似地,如果被調(diào)用的協(xié)程拋出異常,該異常也會傳導(dǎo)給調(diào)用者。實際上,只要你用 yield 表達式來調(diào)用協(xié)程(否則會阻塞),這個例子就模擬了 Stackless Python 中使用的簡單的子任務(wù)(tasklet)。這只是一個非常簡單的例子,但也可以使用更復(fù)雜的調(diào)度程序。(例如,現(xiàn)有的 GTasklet 框架 (http://www.gnome.org/~gjc/gtasklet/gtasklets.html) 和 peak.events 框架 (http://peak.telecommunity.com/) 已經(jīng)實現(xiàn)類似的調(diào)度功能,但大多數(shù)因為無法將值或異常傳給生成器,而必須使用很尷尬的解決方法。)
import collections class Trampoline: """Manage communications between coroutines""" running = False def __init__(self): self.queue = collections.deque() def add(self, coroutine): """Request that a coroutine be executed""" self.schedule(coroutine) def run(self): result = None self.running = True try: while self.running and self.queue: func = self.queue.popleft() result = func() return result finally: self.running = False def stop(self): self.running = False def schedule(self, coroutine, stack=(), val=None, *exc): def resume(): value = val try: if exc: value = coroutine.throw(value,*exc) else: value = coroutine.send(value) except: if stack: # send the error back to the "caller" self.schedule( stack[0], stack[1], *sys.exc_info() ) else: # Nothing left in this pseudothread to # handle it, let it propagate to the # run loop raise if isinstance(value, types.GeneratorType): # Yielded to a specific coroutine, push the # current one on the stack, and call the new # one with no args self.schedule(value, (coroutine,stack)) elif stack: # Yielded a result, pop the stack and send the # value to the caller self.schedule(stack[0], stack[1], value) # else: this pseudothread has ended self.queue.append(resume)
一個簡單的 echo 服務(wù)器以及用蹦床原理實現(xiàn)的運行代碼(假設(shè)存在 nonblocking_read 、nonblocking_write 和其它 I/O 協(xié)程,該例子在連接關(guān)閉時拋出 ConnectionLost ):
# coroutine function that echos data back on a connected # socket # def echo_handler(sock): while True: try: data = yield nonblocking_read(sock) yield nonblocking_write(sock, data) except ConnectionLost: pass # exit normally if connection lost # coroutine function that listens for connections on a # socket, and then launches a service "handler" coroutine # to service the connection # def listen_on(trampoline, sock, handler): while True: # get the next incoming connection connected_socket = yield nonblocking_accept(sock) # start another coroutine to handle the connection trampoline.add( handler(connected_socket) ) # Create a scheduler to manage all our coroutines t = Trampoline() # Create a coroutine instance to run the echo_handler on # incoming connections # server = listen_on( t, listening_socket("localhost","echo"), echo_handler ) # Add the coroutine to the scheduler t.add(server) # loop forever, accepting connections and servicing them # "in parallel" # t.run()參考實現(xiàn)
實現(xiàn)了本 PEP 中描述的所有功能的原型補丁已經(jīng)可用,參見 SourceForge 補丁 1223381 (https://bugs.python.org/issue1223381)。
該補丁已提交到 CVS,2005年8月 01-02。
致謝Raymond Hettinger (PEP 288) 與 Samuele Pedroni (PEP 325) 第一個正式地提出將值或異常傳遞給生成器的想法,以及關(guān)閉生成器的能力。Timothy Delaney 建議了本 PEP 的標題,還有 Steven Bethard 幫忙編輯了早期的版本。另見 PEP-340 的致謝部分。
參考文獻TBD.
版權(quán)本文檔已經(jīng)放置在公共領(lǐng)域。
源文檔:https://github.com/python/peps/blob/master/pep-0342.txt
----------------(譯文完)--------------------
相關(guān)鏈接:
PEP背景知識 :學習Python,怎能不懂點PEP呢?
PEP翻譯計劃 :https://github.com/chinesehua...
[[譯] PEP 255--簡單的生成器](https://mp.weixin.qq.com/s/vj...
[[譯]PEP 525--異步生成器](https://mp.weixin.qq.com/s/fy...
花下貓語: 嘮叨幾句吧,年前這幾周事情太多了,擠著時間好歹是又翻譯出一篇 PEP。與生成器密切相關(guān)的 PEP 已經(jīng)完成 3/4,年后再譯最后一篇(PEP-380)。當初翻譯第一篇,完全是一時興起,直覺這是一件有意義的事,現(xiàn)在呢,這個念頭開始有點膨脹——我竟然在 Github 上建了個翻譯項目。我深知,自己水平實在有限,因此不求得到多少認同吧。但行好事,莫問前程。不過,若有人幫著吆喝一聲,也是極好的。
-----------------
本文原創(chuàng)并首發(fā)于微信公眾號【Python貓】,后臺回復(fù)“愛學習”,免費獲得20+本精選電子書。
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://www.ezyhdfw.cn/yun/43091.html
摘要:提議以下的新的生成器語法將被允許在生成器的內(nèi)部使用其中表達式作用于可迭代對象,從迭代器中提取元素。子迭代器而非生成器的語義被選擇成為生成器案例的合理泛化。建議如果關(guān)閉一個子迭代器時,引發(fā)了帶返回值的異常,則將該值從調(diào)用中返回給委托生成器。 導(dǎo)語: PEP(Python增強提案)幾乎是 Python 社區(qū)中最重要的文檔,它們提供了公告信息、指導(dǎo)流程、新功能的設(shè)計及使用說明等內(nèi)容。對于學習...
摘要:輔之以事件循環(huán),協(xié)程可用于異步處理,尤其是在中。當前支持的協(xié)程基于增強型生成器,于版本開始采用。新的特性中,異步還有兩種新用途異步內(nèi)容管理器和迭代器。 現(xiàn)在 Python 已經(jīng)支持用協(xié)程進行異步處理。但最近有建議稱添加協(xié)程以全面完善 Python 的語言結(jié)構(gòu),而不是像現(xiàn)在這樣把他們作為生成器的一個類型。此外,兩個新的關(guān)鍵字———異步(async)和等待(await),都該添加到 Pyt...
閱讀 998·2021-11-22 12:09
閱讀 3779·2021-09-27 13:36
閱讀 1465·2021-08-20 09:37
閱讀 4143·2019-12-27 12:22
閱讀 2422·2019-08-30 15:55
閱讀 2423·2019-08-30 13:16
閱讀 2900·2019-08-26 17:06
閱讀 3496·2019-08-23 18:32