標籤:debug detail 解釋 消失 技術 地址 通過 span 這一
作為一個由c/c++轉過來的菜鳥,剛接觸Python的變數的時候很不適應,應為他的行為很像指標,void* ,不知道大家有沒有這樣的感覺。其實Python是以資料為本,變數可以理解為標籤。作為c/c++的菜鳥,把跟蹤變數地址的習慣帶入Python,舉個小例子說明Python的變數,對象,及參數傳遞。
1 ‘‘‘例子1‘‘‘2 x = 13 def fun(x):4 x = 25 return None6 7 fun(x)8 print(x)
其實不列印也可以,我們用pycharm單步調試,看一下在每一行執行中,變數x的值的變化,及其地址的變化(其實這句話應該改成:變數x的指向變化更準確)
Python中id()函數,可以返回對象的地址,id()的官方解釋是:Return the “identity” of an object,既然是identity,肯定是唯一的;官方又說:CPython implementation detail: This is the address of the object in memory。我們暫時把id()傳回值看做是對象在記憶體中的地址。
第一步:進入debug,在監看式視窗,添加對Id(x),和id(y) 的觀察,藍色高亮,表示下一步將執行,我們看到這時,x,y都沒有分配地址
第二步:執行下一步,我們發現變數x,開始分配地址, 1392686144,我們記下這個數。
第三步:進入函數中,執行 x = 2語句,我們發現,x的地址已經變成 1392686176,這就是Python 變數的特性,我們不能理解成把變數x賦值為2,而是“名字為x的標籤指向對象2”,這樣更準確。
第四步:返回fun(x)函數,我們發現X的id()值又變回原來的數字,在這個例子中,我們把局部變數和全域變數用同一個標籤指示,當調用函數,進入函數內部執行時,系統會建立堆棧,保留進入函數前的運行環境及資料。進入函數後,有建立了一個同名的標籤x,x = 2,把局部標籤指向局部對象2,這是局部標籤x指向一個新的對象,記憶體位址肯定變化,當return none,返回函數調用時,堆棧撤銷,局部的對象,變數隨之撤銷,局部標籤x也撤銷,此時x做回自己,變成全域標籤x,依舊指向數字對象1.這就是為什麼在函數內部,標籤x指向其他對象後,返回調用,又恢複調用前的記憶體位址。
第一個例子中,從標籤x的記憶體位址變化,幫我們理解Python的變數的行為。
在第二個例子中,我們仍然通過監視標籤的記憶體位址變化,理解參數傳遞的過程
1 ‘‘‘例子2‘‘‘2 a = []3 def fun(a):4 a.append(1)5 return None6 7 fun(a)8 print(a)
第一步:執行完函數調用,參數賦值,藍色高亮是下一步將要執行的代碼。我們發現在這一步,發生了參數賦值,建立堆棧,局部標籤x的記憶體位址與外部標籤a的記憶體位址相同,說明這一步,完成參數賦值,我們是不是可以把Python的“指派陳述式”理解為“標籤指向”這個動作?從這看,這樣理解是可以的。所以“參數賦值”這個動作,可以理解為統一標籤指向。
第二步: 當函數返回時,我們發現列表a的地址沒有改變,並且列表中元素1得到保留,沒有因為局部變數撤銷而消失,這回一個典型的通過標籤(引用),在局部過程中改變全域變數的例子。Python標籤的這種特性是不是很像c++中的引用?是不是很像c中的指標?
總結:Python的變數,我們用標籤來理解,參考c的void*,參考c++中的&,Python的賦值動作,可以理解為“標籤改變指向”的動作。參數傳遞過程,是交換標籤指向的過程
python的變數,對象的記憶體位址以及參數傳遞過程