如何用python生成软件_python中的生成器函数是如何工作的?
1. python中的普通函數是怎么運行的?
當一個python函數在執行時,它會在相應的python棧幀上運行,棧幀表示程序運行時函數調用棧中的某一幀。想要獲得某個函數相關的棧幀,則必須在調用這個函數且這個函數尚未返回時獲取,可能通過inspect模塊的currentframe()函數獲取當前棧幀。
棧幀對象中的3個常用的屬性:
f_back : 調用棧的上一級棧幀
f_code: 棧幀對應的c
f_locals: 用在當前棧幀時的局部變量;
比如:
更進一步講, 標準的python解釋器是用C語言寫的,通常稱作CPython, 當執行一個python函數時,解釋器中的C函數 PyEval_EvalFrameEx() 就會被調用,它來處理python 代碼的字節碼, 它的參數為對于python函數的棧幀 object,即上面例子中的 x就是一個棧幀對象。
舉例說明函數是如何運行的?
使用dis模塊查看一下函數foo()的字節碼(看不懂內容沒事,其它有規律):
運行過程:
解釋器調用 C函數 PyEval_EvalFrameEx()運行foo()的字節碼,它的參數為foo()對應的棧幀對象,運行位置為foo()對應的棧幀; 在運行過程中,遇到 CALL_FUNCTION 時,它會為函數bar()生成新的棧幀,然后又調用一個 PyEval_EvalFrameEx() 運行bar()對應的字節碼,……,如此遞歸,然后一層層的返回;
2. 對于python中棧幀:
在python中的棧幀其實是在解釋器的堆上分配內存的,所以,在一個python函數運行完成后,它的棧幀的仍然存在,并沒有消失,下面例子說明了(當func函數運行完成后,我們然后可以訪問到它對應的棧幀):
3. python中的生成器函數是怎么運行的?
對于函數與生成器函數的區別在于生成器中有yield表達式, 它們的co_flags是不相同的:
function沒有*args或**kw時,func.__ code__.co_flags=67;
function有*args沒有**kw時,func.__ code__.co_flags=71;
function沒有*args有**kw時,func.__ code__.co_flags=75;
function既有*args也有**kw時,func.__ code__.co_flags=79;
function是一個generator時,func.__ code__.co_flags=99.
當運行一個生成器函數時,它會生成一個生成器
上面例子中生成了兩個生成器a與b, 每一個生成器都有兩個常用的屬性,分別為gi_frame與gi_code, 不同的生成器的gi_code是相同的,對應生成器函數的字節碼,然而它們的gi_frame是不相同的,所以,不同的生成器可以分別運行,并且互不干擾;
對于每一個棧幀又都有一個指針f_lasti,它指向了最后執行的命令,在一開始沒有執行時,它的值為-1;
當生成器執行到最后時,它就產生一個 StopIteration 異常,然后就停止了,當生成器函數中有return時, 這個異常的值就是return的值,如果沒有return,異常的值為空;
生成器函數就就是這么運行的。
4.生成器相關操作:
1.X.__ next__()方法和next()內置函數
當我們調用一個生成器函數時來生成一個生成器X時,這個生成器對象就會自帶一個X.__ next__()方法,它可以開始或繼續函數并運行到下一個yield結果的返回或引發一個StopIteration異常(這個異常是在運行到了函數末尾或著遇到了return語句的時候引起)。也可以通過python的內置函數next()來調用X.__ next__()方法,結果都是一樣的;
2.生成器函數協議中的send()方法
在講send()方法的時候,有必要了解一下next()或__next__()或send()語句執行時,生成器內的程序執行到了哪里暫停了。寫一個很簡單的函數,使用pdb調試一下:
在第7行設置一個斷點
通過看上面的程序,我們知道,當next()或__next__()或send()語句執行時,在生成器里面的程序中它執行到 yiled value 這條語句, 它yield出來了一個value值,但是沒有執行yiled value表達式 的返回值它就暫停了;
現在說說send()方法:從技術上講,yield是一個表達式,它是有返回值的,當我們使用內置的next()函數或__next__方法時,默認yield表達式的返回值為 None,它使用send(value)方法時,它可以把一個值傳遞給生成器,使得yield表達式的返回值為send()方法傳入的值; 當我們第一次執行send()方法時,我們必須傳入None值,因為第一次執行時,還沒有等待返回值的yield表達式(雖然 send()方法會執行下一條yield語句,但是上面已經說明了它在還沒有來得及執行yiled value表達式 的返回值時它就暫停了)
定義一個gen.py文件,里面的內容為:
3.生成器函數中的return 語句:
當生成器運行到了return語句時,會拋出StopIteration的異常,異常的值就是return的值; 另外,即使return后面有yield語句,也不會被執行;
4.另外,一個生成器對象也有close方法與throw方法,可以使用它們提前關閉一個生成器或拋出一個異常;使用close方法時,它本質上是在生成器內部產生了一個終止迭代的GeneratorExit的異常;
5. 最后一個要講的內容:yield from
這個是在python3.0以后新增加的內容,可以讓生成器delegate另一個生成器;
1.舉一個例子看看它是怎么往外 yield數據的???
這個例子我們明白了兩點:1. 當我們調用主生成器caller時,遇到yield from 時,它就會停下來,運行子生成器的程序, yield出來的數據就是子生成器里的數據;2. yield from 表達式的返回值為子生成器的return的值;
2.舉個例子看看它是怎么通過 send()方法往里傳遞數據的?
通過這個例子,我們明白了1點:當主生成器遇到yield from以后,我們通過 send()方法傳入值最終傳給了子生成器;
3.通過 yield from ,可以嵌套調用生成器,比如:
總結
以上是生活随笔為你收集整理的如何用python生成软件_python中的生成器函数是如何工作的?的全部內容,希望文章能夠幫你解決所遇到的問題。
                            
                        - 上一篇: python中df去重_Python中D
 - 下一篇: javascript 校验 非空_Jav