變量與參數傳遞
理解變量
拋棄變量是存儲數據的盒子這一錯誤觀念,
可以把python變量理解為附加在對象上的標注
來個栗子
a = [1, 2, 3] b = a a.append(4) print(a, b) #[1, 2, 3, 4] [1, 2, 3, 4] print(a is b)
說明:a和b引用同一個列表,而不是這個列表的副本,當為a添加一個元素時,a,b都發生改變並指向同一個列表,
很明顯用變量是存儲數據的盒子這一觀念無法解釋。
變量賦值
在python中,通常會說把某個變量分配給某個對象,絕不會說把某個對象分配給某個變量,因為對象在賦值之前就創建了。
a = 3
說明:對於python中的賦值語句,應該先讀右邊,對象在右邊創建或獲取,之后將左邊的變量綁定到對象上,
這就像往對象上貼上標注。
變量只不過是標注,你無法阻止往對象上貼多個標注,貼的多個標注就是別名。
每個變量都有標識、類型和值,對象一旦創建它的標識就不會變,標識可以理解為對象在內存中的地址。
id()函數返回對象內存地址的整數表示,id是唯一的數值標識,在對象的生命周期中不會改變
標識通常用is運算符檢查
==比較的是兩個對象中保存的數據,
is比較的是兩個對象的標識,is常用於變量與單例值之間的比較:
x is None: x is not None:
變量分類
在python中,一切都是對象,變量存儲的是對象的引用。
對象可以分為可變對象和不可變對象,可變與不可變是針對對象內容本身而言的。
對象分類 | 概念 | 包括 |
可變對象 | 對象創建后對象的內容是可以改變的 | list, dict, set |
不可變對象 | 對象創建后對象的內容是不可以改變的 | bool, int, float, tuple, str, frozens |
栗子
不可變對象
x = 1 y = x print(id(x), id(y), id(1)) #postion 1 (8791467483984 8791467483984 8791467483984) x += 1 print(id(x), id(y), id(1)) #postion 2 (8791467484016 8791467483984 8791467483984)
x, y同時指向對象1,在position1位置,id(x), id(y), id(1)的內存地址一樣
x += 1, 開辟了新的內存空間,創建對象2,讓x重新指向新的對象2,但是原來的對象1和對象1的內存地址並沒有發生變化
優點:
減少重復對象對內存空間的占用
可變對象
可變對象的修改並不會開辟新的內存空間,而是直接在原內存空間上直接修改對象的內容
即對象的內存地址並不會改變,改變的是對象的內容
list1 = [1, 2] print(list1, id(list1)) #position1 ([1, 2] 37249672) list1.append(3) print(list1, id(list1)) #position2 ([1, 2, 3] 37249672)
參數傳遞
python唯一支持的參數傳遞模式是共享傳參。
共享傳參指函數中的各個形參獲得對應實參中各個引用的副本,即函數內部的形參是實參的別名。
x = 1 y = [1, 2] def fun(a, b): print(a is x, b is y) fun(x, y) #True True
說明:當調用函數fun(x, y)時,a和x共同指向數字1,b和y共同指向列表[1, 2],即形參a是實參x的別名,形參b是實參y的別名。
注意:不要使用可變的對象作為參數的默認值,否則可能會得到意想不到的結果。
def extend_element(ele, eles=[]): eles.append(ele) return eles list1 = extend_element('a') list2 = extend_element(2, [1]) list3 = extend_element('b') print(list1, '->', list2, '->', list3) #['a', 'b'] -> [1, 2] -> ['a', 'b'] print(list1 is list3) #True
說明:我們發現list1和list3指向同一個列表,這是為什么呢?
其實這個根源在於,默認值在定義函數時計算,通常在模塊加載時,因此默認值變成了函數對象的一個屬性。