Python3中生成器介绍
? ? ? 生成器(generator):一個返回生成器迭代器的函數。它看起來像一個普通函數,除了它包含用于生成一系列可在for循環中使用的值的yield表達式或者可以使用next函數一次檢索一個值。
? ? ? 在Python中,使用了yield的函數被稱為生成器。跟普通函數不同的是,生成器是一個返回迭代器的函數,只能用于迭代操作。生成器函數一般是通過for循環調用,for循環自帶next方法。
? ? ? 生成器迭代器(generator iterator):由生成器函數(generator function)創建的對象。每個yield暫時掛起處理,記住位置執行狀態(包括局部變量和掛起的try語句)。當生成器迭代器恢復時,它從停止的地方開始(與每次調用都重新開始的函數相反)。
? ? ? 生成器表達式(generator expression):返回迭代器的表達式。它看起來像一個普通表達式,后跟一個定義循環變量、范圍和可選if子句的for子句。
? ? ? yield 表達式(yield expression):用在定義生成器函數或異步生成器函數,它只能在函數定義體中使用。在函數體中使用yield表達式會使該函數成為生成器。
? ? ? 當一個生成器函數被調用時,它返回一個稱為生成器的迭代器。該生成器控制生成器函數的執行。當調用生成器的任一方法時,執行開始。此時,繼續執行第一個yield表達式,在那里它再次掛起,將expression_list的值返回給生成器的調用者。掛起是指保留所有局部狀態,包括局部變量的當前綁定、指令指針、內部計算堆棧以及任何異常處理的狀態。當通過調用生成器的任一方法恢復執行時,該函數可以像yield表達式被另一個外部調用繼續執行。恢復后的yield表達式的值取決于恢復執行的方法。如果使用__next()__(通常通過for或next()內置函數),則結果為None。否則,如果使用send(),則結果將是傳遞給該方法的值。
? ? ? 在try結構中的任何地方都允許使用yield表達式。如果生成器在完成之前沒有恢復,生成器迭代器的close()方法將被調用,允許任何掛起的finally子句執行。
? ? ? 當使用yield from <expr>時,提供的表達式必須是可迭代的。迭代產生的值直接傳遞給當前生成器方法的調用者。如果底層迭代器有適當的方法,任何用send()傳入的值和用throw()傳入的任何異常都被傳遞給底層迭代器。如果不是這種情況,則send()將觸發AttributeError或TypeError,而throw()將立即觸發傳入的異常。當底層迭代器完成時,觸發的StopIteration實例的value屬性成為yield表達式的值。它可以在觸發StopIteration時顯式設置,也可以在子迭代器是生成器時自動設置。
? ? ? yield語句:在語義上等同于yield表達式。yield表達式和語句僅用在定義生成器函數中。在函數中使用yield便會創建生成器函數。
? ? ? yield語句暫停函數的執行并將一個值發送回調用者,但保留足夠的狀態以使函數能夠從停止的地方恢復。恢復后,該函數會在最后一次yield運行后立即繼續執行。這允許它的代碼隨著時間的推移產生一系列值,而不是一次計算它們并像列表一樣發送它們。
? ? ? 當yield表達式是賦值語句右側的唯一表達式時,可以省略括號。
? ? ? 生成器迭代器方法:它們可用于控制生成器函數的執行。當生成器已經在執行時調用下面的任何生成器方法都會觸發ValueError異常。
? ? ? (1).generator.__next__():開始執行生成器函數或在最后一次執行的yield表達式處繼續執行。當使用__next__()方法恢復生成器函數時,當前的yield表達式總是計算為None。然后繼續執行下一個yield表達式,在那里生成器再次掛起,并且expression_list的值返回給__next__()的調用者。如果生成器退出而沒有產生另一個值,則會觸發StopIteration異常。此方法通常被隱式調用,例如通過for循環或內置的next()函數。
? ? ? (2).generator.send(value):恢復執行并將value ”sends”到生成器函數中。value參數成為當前yield表達式的結果。send()方法返回生成器生成的下一個值,或者如果生成器退出而沒有生成另一個值則觸發StopIteration。當調用send()來啟動生成器時,必須以None作為參數調用它,因為沒有可以接收值的yield表達式。
? ? ? (3).generator.throw(type[, value[, traceback]]):在生成器暫停時觸發類型的異常,并返回生成器函數產生的下一個值。如果生成器退出而沒有產生另一個值,則會觸發StopIteration異常。如果生成器函數沒有捕獲傳入的異常,或者觸發不同的異常,則該異常會傳遞給調用者。
? ? ? (4).generator.close():在生成器函數暫停的位置觸發GeneratorExit。如果生成器函數隨后正常退出、已關閉或觸發GeneratorExit,則close返回到其調用者。如果生成器產生一個值,則會觸發RuntimeError。如果生成器觸發任何其它異常,則會將其傳遞給調用者。如果生成器由于異常或正常退出而退出,則close()不執行任何操作。
? ? ? 以上內容主要翻譯于: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函數并未真的執行,返回generator對象print("object:", generator)print(next(generator)) # 當調用next或__next__時,echo函數才正式開始執行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
總結
以上是生活随笔為你收集整理的Python3中生成器介绍的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Linux下addr2line命令用法
- 下一篇: libuvc介绍及简单使用