一文探索CPython的變量實現(xiàn)機制
在Python中,變量的使用看起來非常簡單,例如 a = 10,s = "hello"等等。
然而,這種簡單的賦值操作背后,CPython其實做了很多復雜的工作。
本文將通過一些簡單易懂的代碼示例,一起探索Python變量背后的奧秘,讓我們對它的實現(xiàn)機制有更深一步的理解。
1. 變量到底是什么?
在Python中,變量本質(zhì)上是一個名字到值的映射。
例如,當你寫a = 1時,a是一個名字,而1是一個值。
CPython會將這個名字和值關(guān)聯(lián)起來,以便你后續(xù)可以通過名字訪問這個值。
a = 1 print(a) # 輸出:1
這種映射關(guān)系是通過一個名為命名空間的結(jié)構(gòu)實現(xiàn)的。
命名空間是一個字典,其中的鍵是變量名,值是變量對應的對象。
它的定義可參考CPython源碼中的Include/internal/pycore_frame.h文件。
typedef struct _PyInterpreterFrame {
// 省略... ...
PyObject *f_globals; /* Borrowed reference. Only valid if not on C stack */
PyObject *f_builtins; /* Borrowed reference. Only valid if not on C stack */
PyObject *f_locals; /* Strong reference, may be NULL. Only valid if not on C stack */
// 省略... ...
}
其中,f_locals 保存局部變量映射,函數(shù)執(zhí)行時,局部變量值存于此;
f_globals 用于全局變量,模塊級代碼塊執(zhí)行時,f_globals 指向模塊全局命名空間字典;
f_builtins 關(guān)聯(lián)內(nèi)置命名空間。
2. 變量的底層實現(xiàn):字節(jié)碼
CPython在執(zhí)行代碼時,會先將代碼編譯成字節(jié)碼,然后由虛擬機執(zhí)行這些字節(jié)碼。我們可以通過 dis 模塊查看代碼的字節(jié)碼。
例如,對于a = 1,字節(jié)碼如下:
import dis code = """ a = b """ dis.dis(code)

LOAD_NAME:從命名空間中加載變量b的值STORE_NAME:將值存儲到變量a中
這兩個指令展示了CPython如何處理變量的讀取和賦值。
3. 命名空間與作用域
Python中的變量存儲在不同的命名空間中,而這些命名空間又與代碼的作用域相關(guān),作用域決定了變量的可見性。
Python有三種主要的作用域:
- 局部作用域:函數(shù)內(nèi)部的變量
- 全局作用域:模塊級別的變量
- 內(nèi)置作用域:包含內(nèi)置函數(shù)和類型的命名空間
x = "global" # 全局變量
def func():
y = "local" # 局部變量
print(x) # 輸出:global
print(y) # 輸出:local
func()

在這個例子中,x是全局變量,y是局部變量。
如果在函數(shù)中嘗試訪問一個未定義的變量,CPython會按照以下順序查找:
- 局部命名空間(
f_locals) - 全局命名空間(
f_globals) - 內(nèi)置命名空間(
f_builtins)
如果仍然找不到,就會拋出NameError異常。
4. 不同變量的字節(jié)碼
CPython為不同作用域的變量提供了不同的字節(jié)碼指令,以優(yōu)化性能和實現(xiàn)特定的行為。
4.1. 局部變量
在函數(shù)中,局部變量使用LOAD_FAST和STORE_FAST指令。
這些指令直接操作一個數(shù)組,而不是字典,因此速度更快。
def func():
a = 1 # STORE_FAST
b = a # LOAD_FAST
return b
dis.dis(func)

4.2. 全局變量
全局變量使用LOAD_GLOBAL和STORE_GLOBAL指令。
這些指令會直接操作全局命名空間。
x = 1
def func():
global x
x = 2 # STORE_GLOBAL
return x # LOAD_GLOBAL
dis.dis(func)

4.3. 閉包變量
當函數(shù)嵌套時,內(nèi)部函數(shù)可以訪問外部函數(shù)的變量。
這些變量稱為閉包變量,使用LOAD_DEREF和STORE_DEREF指令。
def outer():
x = 1
def inner():
return x # LOAD_DEREF
return inner
dis.dis(outer)

5. 類中的變量
在類定義中,變量的行為與函數(shù)不同。
類定義中的變量使用LOAD_NAME和STORE_NAME指令,因為類的命名空間會動態(tài)地與全局命名空間交互。
x = "global"
class MyClass:
print(x) # 使用 LOAD_NAME
x = "local"
print(x) # 使用 LOAD_NAME
MyClass()
輸出:

查看指令的話,可以使用:python.exe -m dis .\cpython-variable.py命令。
如果在類中使用嵌套函數(shù),CPython會使用LOAD_CLASSDEREF指令來處理閉包變量。
class MyClass:
x = "cell"
def method(self):
print(x) # 使用 LOAD_CLASSDEREF
MyClass().method()
6. 編譯器如何選擇指令
CPython的編譯器會根據(jù)變量的作用域和代碼塊類型選擇合適的字節(jié)碼指令。
例如:
- 如果變量是局部變量,編譯器會生成
LOAD_FAST和STORE_FAST - 如果變量是全局變量,編譯器會生成
LOAD_GLOBAL和STORE_GLOBAL - 如果變量是閉包變量,編譯器會生成
LOAD_DEREF和STORE_DEREF
7. 總結(jié)
Python變量的實現(xiàn)機制比看起來復雜得多,它涉及到字節(jié)碼指令、命名空間、作用域以及編譯器的決策邏輯。
通過理解這些概念,可以更好地掌握Python的變量行為,尤其是在復雜的作用域場景中。
如果對CPython的實現(xiàn)感興趣,可以進一步閱讀其源碼中與變量相關(guān)的部分。
以上就是一文探索CPython的變量實現(xiàn)機制的詳細內(nèi)容,更多關(guān)于CPython變量的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
Python應用領(lǐng)域和就業(yè)形勢分析總結(jié)
在本篇文章總我們給大家整理了關(guān)于Python應用領(lǐng)域和就業(yè)形勢分析以及圖文介紹,需要的朋友們可以參考下。2019-05-05
使用PyTorch處理多維特征輸入數(shù)據(jù)的完美實現(xiàn)
在機器學習和深度學習領(lǐng)域,我們經(jīng)常會面對具有多維特征輸入的問題,這種情況出現(xiàn)在各種應用中,包括圖像識別、自然語言處理、時間序列分析等,PyTorch是一個強大的深度學習框架,在本篇博客中,我們將探討如何使用PyTorch來處理多維特征輸入數(shù)據(jù)2023-10-10

