python中的引用傳遞
首先必須理解的是,python中一切的傳遞都是引用(地址),無論是賦值還是函式呼叫,不存在值傳遞。
可變物件和不可變物件
python變數儲存的是物件的引用,這個引用指向堆記憶體裡的物件,在堆中分配的物件分為兩類,一類是可變物件,一類是不可變物件。不可變物件的內容不可改變,保證了資料的不可修改(安全,防止出錯),同時可以使得在多執行緒讀取的時候不需要加鎖。
不可變物件(變數指向的記憶體的中的值不能夠被改變)
當更改該物件時,由於所指向的記憶體中的值不可改變,所以會把原來的值複製到新的空間,然後變數指向這個新的地址。python中數值型別(int和float),字串str,元組tuple都是不可變物件。
下面以int型別為例簡單介紹。
a = 1
print id(a) //40133000L,整數1放在了地址為40133000L的記憶體中,a變數指向這個地址。
a += 1
print id(a) //40132976L,整數int不可改變,開闢新空間存放加1後的int,a指向這個新空間。
可變物件(變數指向的記憶體的中的值能夠被改變)
當更改該物件時,所指向的記憶體中的值直接改變,沒有發生複製行為。python中列表list,字典dict,集合set都是可變物件。下面以list型別為例簡單介紹。
a = [1,2,3]
print id(a) //44186120L。
a += [4,5] //相當於呼叫了a.extend([4])
print id(a) //44186120L,列表list可改變,直接改變指向的記憶體中的值,沒開闢新空間。
a = a + [7,8] //直接+和+=並不等價,使用+來操作list時,得到的是新的list,不指向原空間。
print id(a) //44210632L
引用傳遞後的改變
a = [1,2,3]
b = a
b[0] = 2 //由於list是可變物件,改變b時候會導致a的改變,a和b都是[2,2,3]
s = `abc`
s2 = s
s2 += `d` //由於str是不可變物件,s2是新建的物件,s2的修改不會影響s。s為`abc`,s2為`abcd`。
list注意點
a = [1,2,3]
b = a
a is b //True,因為按引用傳遞,a和b存的地址(引用)是一樣的,改變b相當於改變a。
b = a[:]
a is b //False,想使用list的值卻不想修改原list時可以使用[:]拷貝一份到新空間。
a =[ [0]*2 ]* 2 //以這種方式建立一個二維list,此時a為[[0,0],[0,0]]。
a[0] == a[1] //True,這種建立方法的機制是複製list,所以2個list其實是同一個list。
a[0][0] = 1 //改變第一個list時第二個list也改變,此時a為[[1,0],[1,0]]。
a[0] += [1] //改變第一個list時第二個list也改變,此時a為[[1,0,1],[1,0,1]]。
a[0] = [1,2] //a[0]指向建立的新list[1,2]。此時a[1]不變,a為[[1,2],[1,0,1]]。