Python3中生成器介绍
? ? ? 生成器(generator):一個返回生成器迭代器的函數(shù)。它看起來像一個普通函數(shù),除了它包含用于生成一系列可在for循環(huán)中使用的值的yield表達式或者可以使用next函數(shù)一次檢索一個值。
? ? ? 在Python中,使用了yield的函數(shù)被稱為生成器。跟普通函數(shù)不同的是,生成器是一個返回迭代器的函數(shù),只能用于迭代操作。生成器函數(shù)一般是通過for循環(huán)調(diào)用,for循環(huán)自帶next方法。
? ? ? 生成器迭代器(generator iterator):由生成器函數(shù)(generator function)創(chuàng)建的對象。每個yield暫時掛起處理,記住位置執(zhí)行狀態(tài)(包括局部變量和掛起的try語句)。當(dāng)生成器迭代器恢復(fù)時,它從停止的地方開始(與每次調(diào)用都重新開始的函數(shù)相反)。
? ? ? 生成器表達式(generator expression):返回迭代器的表達式。它看起來像一個普通表達式,后跟一個定義循環(huán)變量、范圍和可選if子句的for子句。
? ? ? yield 表達式(yield expression):用在定義生成器函數(shù)或異步生成器函數(shù),它只能在函數(shù)定義體中使用。在函數(shù)體中使用yield表達式會使該函數(shù)成為生成器。
? ? ? 當(dāng)一個生成器函數(shù)被調(diào)用時,它返回一個稱為生成器的迭代器。該生成器控制生成器函數(shù)的執(zhí)行。當(dāng)調(diào)用生成器的任一方法時,執(zhí)行開始。此時,繼續(xù)執(zhí)行第一個yield表達式,在那里它再次掛起,將expression_list的值返回給生成器的調(diào)用者。掛起是指保留所有局部狀態(tài),包括局部變量的當(dāng)前綁定、指令指針、內(nèi)部計算堆棧以及任何異常處理的狀態(tài)。當(dāng)通過調(diào)用生成器的任一方法恢復(fù)執(zhí)行時,該函數(shù)可以像yield表達式被另一個外部調(diào)用繼續(xù)執(zhí)行。恢復(fù)后的yield表達式的值取決于恢復(fù)執(zhí)行的方法。如果使用__next()__(通常通過for或next()內(nèi)置函數(shù)),則結(jié)果為None。否則,如果使用send(),則結(jié)果將是傳遞給該方法的值。
? ? ? 在try結(jié)構(gòu)中的任何地方都允許使用yield表達式。如果生成器在完成之前沒有恢復(fù),生成器迭代器的close()方法將被調(diào)用,允許任何掛起的finally子句執(zhí)行。
? ? ? 當(dāng)使用yield from <expr>時,提供的表達式必須是可迭代的。迭代產(chǎn)生的值直接傳遞給當(dāng)前生成器方法的調(diào)用者。如果底層迭代器有適當(dāng)?shù)姆椒?#xff0c;任何用send()傳入的值和用throw()傳入的任何異常都被傳遞給底層迭代器。如果不是這種情況,則send()將觸發(fā)AttributeError或TypeError,而throw()將立即觸發(fā)傳入的異常。當(dāng)?shù)讓拥魍瓿蓵r,觸發(fā)的StopIteration實例的value屬性成為yield表達式的值。它可以在觸發(fā)StopIteration時顯式設(shè)置,也可以在子迭代器是生成器時自動設(shè)置。
? ? ? yield語句:在語義上等同于yield表達式。yield表達式和語句僅用在定義生成器函數(shù)中。在函數(shù)中使用yield便會創(chuàng)建生成器函數(shù)。
? ? ? yield語句暫停函數(shù)的執(zhí)行并將一個值發(fā)送回調(diào)用者,但保留足夠的狀態(tài)以使函數(shù)能夠從停止的地方恢復(fù)。恢復(fù)后,該函數(shù)會在最后一次yield運行后立即繼續(xù)執(zhí)行。這允許它的代碼隨著時間的推移產(chǎn)生一系列值,而不是一次計算它們并像列表一樣發(fā)送它們。
? ? ? 當(dāng)yield表達式是賦值語句右側(cè)的唯一表達式時,可以省略括號。
? ? ? 生成器迭代器方法:它們可用于控制生成器函數(shù)的執(zhí)行。當(dāng)生成器已經(jīng)在執(zhí)行時調(diào)用下面的任何生成器方法都會觸發(fā)ValueError異常。
? ? ? (1).generator.__next__():開始執(zhí)行生成器函數(shù)或在最后一次執(zhí)行的yield表達式處繼續(xù)執(zhí)行。當(dāng)使用__next__()方法恢復(fù)生成器函數(shù)時,當(dāng)前的yield表達式總是計算為None。然后繼續(xù)執(zhí)行下一個yield表達式,在那里生成器再次掛起,并且expression_list的值返回給__next__()的調(diào)用者。如果生成器退出而沒有產(chǎn)生另一個值,則會觸發(fā)StopIteration異常。此方法通常被隱式調(diào)用,例如通過for循環(huán)或內(nèi)置的next()函數(shù)。
? ? ? (2).generator.send(value):恢復(fù)執(zhí)行并將value ”sends”到生成器函數(shù)中。value參數(shù)成為當(dāng)前yield表達式的結(jié)果。send()方法返回生成器生成的下一個值,或者如果生成器退出而沒有生成另一個值則觸發(fā)StopIteration。當(dāng)調(diào)用send()來啟動生成器時,必須以None作為參數(shù)調(diào)用它,因為沒有可以接收值的yield表達式。
? ? ? (3).generator.throw(type[, value[, traceback]]):在生成器暫停時觸發(fā)類型的異常,并返回生成器函數(shù)產(chǎn)生的下一個值。如果生成器退出而沒有產(chǎn)生另一個值,則會觸發(fā)StopIteration異常。如果生成器函數(shù)沒有捕獲傳入的異常,或者觸發(fā)不同的異常,則該異常會傳遞給調(diào)用者。
? ? ? (4).generator.close():在生成器函數(shù)暫停的位置觸發(fā)GeneratorExit。如果生成器函數(shù)隨后正常退出、已關(guān)閉或觸發(fā)GeneratorExit,則close返回到其調(diào)用者。如果生成器產(chǎn)生一個值,則會觸發(fā)RuntimeError。如果生成器觸發(fā)任何其它異常,則會將其傳遞給調(diào)用者。如果生成器由于異常或正常退出而退出,則close()不執(zhí)行任何操作。
? ? ? 以上內(nèi)容主要翻譯于:7. Simple statements — Python 3.9.7 documentation
? ? ?測試代碼如下:
var = 1
if var == 1:# reference: https://docs.python.org/3/reference/expressions.html#yieldexprdef echo(value=None):print("Execution starts when 'next()' is called for the first time.")try:while True:try:value = (yield value)print("value:", value)except Exception as e:value = efinally:print("Don't forget to clean up when 'close()' is called.")generator = echo(1) # 此處echo函數(shù)并未真的執(zhí)行,返回generator對象print("object:", generator)print(next(generator)) # 當(dāng)調(diào)用next或__next__時,echo函數(shù)才正式開始執(zhí)行print(next(generator))print("start send"); print(generator.send(10)); print("end send")generator.throw(TypeError, "spam")generator.close()
elif var == 2:# reference: https://stackoverflow.com/questions/231767/what-does-the-yield-keyword-domylist = [x*x for x in range(3)] # mylist is an iterable, you store all the values in memoryfor i in mylist:print(i)# Generators are iterators, a kind of iterable you can only iterate over once.# Generators do not store all the values in memory, they generate the values on the flymygenerator = (x*x for x in range(3))for i in mygenerator:print(i)for i in mygenerator:print(i) # 第二次for in不會有任何值輸出, generators can only be used once# yield is a keyword that is used like return, except the function will return a generatordef create_generator():print("start ...")mylist = range(3)for i in mylist:yield i*imygenerator2 = create_generator() # create a generatorprint("object:", mygenerator2) # mygenerator2 is an objectprint("value:", mygenerator2.__next__())for i in mygenerator2:print(i)for i in mygenerator2:print(i) # 第二次for in不會有任何值輸出# To master yield, you must understand that when you call the function, the code you have# written in the function body does not run. The function only returns the generator object.# Then, your code will continue from where it left off each time for uses the generator.
elif var == 3:# reference: https://www.geeksforgeeks.org/use-yield-keyword-instead-return-keyword-python/# The yield statement suspends function’s execution and sends a value back to the caller, but retains enough# state to enable function to resume where it is left off. When resumed, the function continues execution# immediately after the last yield run.def simpleGeneratorFun():yield 1yield 2yield 3for value in simpleGeneratorFun():print(value)# An infinite generator function that prints next square number. It starts with 1def nextSquare():i = 1# An Infinite loop to generate squareswhile True:yield i*ii += 1 # Next execution resumes from this pointprint("object:", nextSquare())for num in nextSquare():if num > 100:breakprint(num) # the first value is 1print("go on:")print("object:", nextSquare())for num in nextSquare():if num > 200:breakprint(num) # note: the first value is still 1, instead of 121print("test finish")
? ? ? GitHub:https://github.com/fengbingchun/Python_Test
總結(jié)
以上是生活随笔為你收集整理的Python3中生成器介绍的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Linux下addr2line命令用法
- 下一篇: libuvc介绍及简单使用