本文爲senlie原創,轉載請保留此地址:http://blog.csdn.net/zhengsenliepython
1.
Python虛擬機會從編譯獲得的PyCodeObject對象中依次讀入每一條字節碼指令。
並在當前的上下文環境中運行這條字節碼指令。數組
Python虛擬機其實是在模擬操做中運行文件的過程
PyCodeObject對象中包括了字節碼指令以及程序的所有靜態信息,但沒有包括
程序執行時的動態信息——執行環境(PyFrameObject)
2.Python源代碼中的PyFrameObject
閉包
typedef struct _frame{ PyObject_VAR_HEAD //"執行時棧"的大小是不肯定的 struct _frame *f_back; //執行環境鏈上的前一個frame。很是多個PyFrameObject鏈接起來造成執行環境鏈表 PyCodeObject *f_code; //PyCodeObject 對象,這個frame就是這個PyCodeObject對象的上下文環境 PyObject *f_builtins; //builtin名字空間 PyObject *f_globals; //global名字空間 PyObject *f_locals; //local名字空間 PyObject **f_valuestack; //"執行時棧"的棧底位置 PyObject **f_stacktop; //"執行時棧"的棧頂位置 //... int f_lasti; //上一條字節碼指令在f_code中的偏移位置 int f_lineno; //當前字節碼相應的源碼行 //... //動態內存,維護(局部變量+cell對象集合+free對象集合+執行時棧)所需要的空間 PyObject *f_localsplus[1]; } PyFrameObject;
每一個 PyFrameObject對象都維護了一個 PyCodeObject對象。這代表每一個 PyFrameObject中的動態內存空間
對象都和源碼中的一段Code相相應。框架
Code Block。PyFrameObject,做用域,名字空間好像都與一段代碼段一一相應?
PyFrameObject中的動態內存空間
函數
PyFrameObject *PyFrame_New(PyThreadState *tstate, PyCodeObject *code, PyObject *globals, PyCodeObject *locals) { PyFrameObject *f; Py_ssize_t extras, ncells, nfrees, i; ncells = PyTuple_GET_SIZE(code->co_cellvars); nfrees = PyTuple_GET_SIZE(code->co_freevars); //這四部分構成了 PyFrameObject維護的動態內存區。其大小由extras肯定 extras = code->co_stacksize + code->co_nlocals + ncells + nfrees; f = PyObject_GC_NewVar(PyFrameObject, &PyFrame_Type, extras); //計算初始化時執行時棧的棧頂 extras = code->co_nlocals + ncells + nfrees; //f_valuestack維護執行時棧的棧底,f_stacktop維護執行時棧的棧頂 f->f_valuestack = f->f_localsplus + extras; f->f_stacktop = f->f_valuestack; return f; }在建立 PyFrameObject對象時,額外申請的內存有一部分是給 PyCodeObject對象用的,
在Python中訪問 PyFrameObject對象
8.1.3的caller.py我執行失敗了。在f_globals.keys()處出錯
看不懂frame_getter.py
3.名字、做用域和名字空間
對於python這類動態語言來講,名字的意義遠比C這類的靜態語言大。因爲名字是python
在執行時能夠找到其所相應的東西的惟一途徑。oop
名字: 一個標識符就是一個名字,比方變量名、函數名、類名等
做用域:做用域是指約束起做用的程序正文區域。ui
Python是具備靜態做用域(詞法做用域)的。
在Python中,一個約束在程序正文的某個位置是否起做用。是由該約束在文本中的位置惟一決定的,
而不是執行時動態決定的。spa
名字空間:由一個 PyDictObject對象實現。每一個名字空間都與一個做用域相應
module
1.將邏輯相關的代碼放在一塊兒,實現代碼複用
2.爲系統劃分名字空間(一個module定義了一個獨立的名字空間)
賦值語句會影響名字空間(「擁有賦值行爲、擁有設置對象屬性的行爲」)
1.建立一個對象obj
2.將obj"賦給"一個名字name
.net
a = 1 def f() class A(object) import abc嵌套做用域最內嵌套做用域規則:1.閉包實現 2.編譯器實現LEGB規則:名字引用動做沿着local做用域、嵌套做用域、global做用域、builtin做用域的順序查找函數相應local做用域。module源文件相應global做用域,Python自身定義了最頂層的做用域——builtin做用域幾個有助理解LEGB規則的代碼段
<span style="font-family: Arial, Helvetica, sans-serif; font-size: 12px;"></span><pre name="code" class="cpp">#---------------------- a = 1 #[1]
def f(): a = 2 #[2] print a #[3] 輸出2 print a #[4] 輸出1 #---------------------- #閉包。在運行func = f()的時候。會運行函數f中的def g():語句,這時Python會將約束a = 2與函數g相應的函數對象捆綁在一塊兒線程
#將捆綁後的結果返回
a = 1
def f(): a = 2 def g(): print a #[1]:輸出結果爲2 return g func = f() func() #[2] #----------------------
<pre name="code" class="cpp">#由一個賦值語句引進的名字在這個賦值語句所在的做用域是可見的。在 第9章 Python虛擬機中的通常表達式 這章有說明實現原理 #[1]pint a語句處,a已經存在了這個Code Block相應的PyFrameObject相應的co_names符號表裏了。但由於還沒運行[2]的賦值語句, #在PyFrameObject的f_locals裏並無這個鍵值對,因此在[1]處會出現「local variable 'a' referenced before assignment」的錯誤a = 1def g():print adef f():print a #[1] error: local variable 'a' referenced before assignment a = 2 #[2]print ag()f()#----------------------a = 1def f():global aprint a #輸出結果:1a = 2f()print a #輸出結果:2#----------------------a = 1def f():a = 2def g():global aprint a #輸出結果:1a += 1return gg = f()g()print a #輸出結果:2
/* Interpreter main loop */ PyObject* PyEval_EvalFrame(PyFrameObject *f) { //…… why = WHY_NOT;//指示了在退出for循環時Python運行引擎的狀態 for (;;) { //…… fast_next_opcode: f->f_lasti = INSTR_OFFSET(); /* 得到字節碼指令和參數*/ opcode = NEXTOP(); oparg = 0; /* 假設指令需要參數。得到指令參數*/ if (HAS_ARG(opcode)) oparg = NEXTARG(); dispatch_opcode: switch (opcode) { case NOP: goto fast_next_opcode; case LOAD_FAST: //…… } }
當Python虛擬機開始運行時,會將當前線程狀態對象中的frame設置爲當前的運行環境(frame),而
在創建新的PyFrameObject對象時,則從當前線程的狀態對象中取出舊的frame,創建PyFrameObject鏈表
線程之間的同步靠的是全局解釋鎖(GIL)
//進程 typedef struct _is{ struct _is *next; struct _ts *tstate_head; //模擬進程環境中的線程集合 PyObject *modules; PyObject *sysdict; PyObject *builtins; //... } PyInterpreterState //線程 typedef struct _ts{ struct _ts *next PyInterpreterState *interp; struct _frame *frame; //模擬線程中的函數調用堆棧 int recursion_depth; //... PyObject *dict; //... long thread_id; } PyThreadState;