Python 面試高頻問題:可變數(shù)據(jù)類型和不可變數(shù)據(jù)類型的區(qū)別
Python可變數(shù)據(jù)類型和不可變數(shù)據(jù)類型是一個基礎而且重要的考點。簡單地說:這里的可變和不可變是指當變量改變的時候,數(shù)據(jù)的地址是否會改變!
可變數(shù)據(jù)類型:如果改變了變量的值,相當于是新建了一個對象(即地址會被改變)。
可變數(shù)據(jù)類型:變量的值發(fā)生變化,但是對象的地址不會改變。
不可變數(shù)據(jù)類型:元組,字符串,數(shù)值。
可變數(shù)據(jù)類型:字典,列表,集合。
引用
在講可變數(shù)據(jù)類型和不可變數(shù)據(jù)類型之前我們要講一下引用的概念。python變量保存的是對象的引用,這個引用指向堆內存里的對象,在堆中分配的對象分為兩類,一類是可變對象,一類是不可變對象。例如:s1="abc"。
其實變量s1 就是對象 abc的引用,s1指向了存儲abc的內存地址,如果想看s1的地址值,可以使用函數(shù)id,id會把地址值轉換成十進制。使用print(id(s1))即可,如下圖所示:
不可變數(shù)據(jù)類型
我們以字符串舉例,直接上代碼:
s1="abc"
print(id(s1))
s1="xyz"
print(id(s1))
輸出:
140712532603136
140712532603168
從輸出結果可見改變字符串類型變量的值,地址也會隨之變化。
我們接下來看這個實例,也是面試筆試中經(jīng)常出的題目。
#在上面代碼基礎上,編寫如下代碼:
s2=s1
print(id(s1))
print(id(s2))
輸出:
743316570224
743316570224
可以看到s2=s1 實際上是s2 和s1都指向了同一個地址。
我們繼續(xù),改變s2的值。
s2="def"
print(id(s1))
print(s1)
print(id(s2))
print(s2)
輸出:
879864758384
xyz
879889887984
def
看到這里,我們就能夠理解為什么改變了s2 的值并沒有影響s1的值。因為s1 和s2指向了不同的地址,所以s1的值并沒有被改變!
可變數(shù)據(jù)類型
我們以列表舉例:
l = [1, 2, 3]
print(id(l))
l.remove(1) # 刪除元素
print(id(l))
l.append(4) # 增加元素
print(id(l))
l[1] = '8' # 修改元素
print(id(l))
輸出:
405927907912
405927907912
405927907912
405927907912
可以看到對列表進行增刪改操作,列表的地址都沒有變化,只是改變了變量的值,而不會新建一個對象,變量引用的對象的地址也不會變化。
再看下面這個實例,與前面的字符串賦值實例類似。
l1=['a','b','c']
l2=l1
print(id(l1))
print(id(l2))
l2.append('d')
print("************")
print(id(l1))
print(l1)
print(id(l2))
print(l2)
輸出:
838366483528
838366483528
************
838366483528
['a', 'b', 'c', 'd']
838366483528
['a', 'b', 'c', 'd']
輸出結果這里就不再多做解釋了,因為 l1 和l2的地址相同,所以彼此間會產(chǎn)生影響。
list的拷貝
有的同學可能要問,如果想讓list 像字符串一樣拷貝并生成同值但是不同地址的兩個list,該如何操作呢?其實這個問題的本質是list直接賦值(用 = 是直接賦值)和拷貝的區(qū)別(拷貝又分為淺拷貝和深拷貝),我會再寫一篇文章來詳細介紹淺拷貝和深拷貝的相關知識點,也請大家持續(xù)關注。
這里先介紹一種比較簡單的方法進行拷貝,使用list()構造函數(shù),代碼如下:
l3=['x','y','z']
l4=list(l3)
print(id(l3))
print(id(l4))
l4.append('a')
print(l3)
print(l4)
輸出:
831456302152
831480344136
['x', 'y', 'z']
['x', 'y', 'z', 'a']
從結果可以看到,l3 和l4的地址不同,所以彼此間不會發(fā)生影響。我們還可以通過使用索引,列表生成式,copy()等方式使兩個列表指向不同的列表對象,這里就不再一一介紹了!