1. 程式人生 > >python踩坑記-引用與賦值

python踩坑記-引用與賦值

引用與賦值

在python中的賦值,實際上都是引用,內在的含義就是用一個標籤指向這個記憶體空間

a = 10

這個語句在執行的時候實際上是先在記憶體空間中建立一個10,然後將a這個標籤指向這個記憶體空間,因此這才是完整的引用操作

python中所有的賦值操作都是引用,而不是複製這個記憶體空間建立一個新的空間來存放這個值

淺拷貝和深拷貝

因為存在引用的關係,那麼資料型別可以分為不可變的資料型別和可變的資料型別,基礎資料型別都是不可變的資料型別,因此數字,字串等都是不可變的,python中的元組也是不可變的資料型別,但是元組裡面的內容如果是可變型別則還是可以變

元組,列表很像,那麼元組和列表都存在索引,這就會存在根據索引取值的操作,那麼問題就來了,列表和元組裡面存放的到底是值還是指向這個值的引用

實際上不管是列表還是元組,列表內部存放的全是引用,引用這個方式就像是貼標籤一樣,記憶體就像是一個盒子,裡面存放著具體的值,列表和元組存放著這些標籤

元組的不可更改意味著這些標籤不能被更改,如果標籤直接指向的是記憶體,那麼確實沒有辦法改動,但是如果標籤指向的是另外一個標籤,這種指向確實沒有辦法變動,但是另外一個標籤指向的是一個可變型別的話那麼就意味著指向的值發生變動,這並不違背元組不可變的方式

a = (1,2,3,[4,5,6])
a[3][0] = 'a'
print(a)

這就可以變動

那麼拷貝動作本身而言是複製值,但是如果值本身指向的是另外一個引用,這就會導致copy複製的只是一個引用,如果引用的值發生變化,還是會影響到複製後的值,解決這個問題就是使用深複製deepcopy,這個方法就能夠進行類似遞迴方式的將引用的引用也複製到位,只要是deepcopy,那就不會出現錯亂的情況

new = []
a = {'name': 0}
for i in range(10):
    a['name'] = i
    new.append(a)
print(new)

結果實際上上有點尷尬

字典的key實際上就像一個標籤,列表裡面存放的其實也是標籤,這才是這個輸出錯誤的原因,字典的賦值操作實際上就是把標籤撕下來貼到新的記憶體盒子上,而迴圈的時候每次的i都不同,實際上每次都去貼新盒子,但是append每次都將標籤追加到列表中,而列表裡面存放的全是同一個標籤,這就很尷尬了,大家都指向的是同一個值,最後變數完成,大家都指向了值為9的記憶體空間,所以最後的結果是:

[{
'name': 9}, {'name': 9}, {'name': 9}, {'name': 9}, {'name': 9}, {'name': 9}, {'name': 9}, {'name': 9}, {'name': 9}, {'name': 9}]

函式的引用

在定義函式的時候函式存在形參和實參,當我們外部傳遞給函式的到底是一個值還是一個引用?

實際上我們經常的操作是傳遞給函式某個變數,而變數本身其實是引用,變數並不是值

初學的時候我也有這樣的疑問,有的函式存在返回值,有的函式沒有返回值,有的函式沒有返回值,有的函式對原來的輸入資料就地修改,有的函式並不修改原資料而是返回的新值

a = [10,9,10,20,31,0]
def function(a):
    b = a
    b.sort()
    return b
print(a)
print(function(a))
print(a)

結果:

[10, 9, 10, 20, 31, 0]
[0, 9, 10, 10, 20, 31]
[0, 9, 10, 10, 20, 31]

這就很尷尬了,為啥最後一個a的值被修改了,這就要說說函式傳參到底傳了啥子玩意

實際上當函式發生呼叫的時候表明上看起來傳入的是a,感覺好像是傳進去了一個列表,實際上傳進去的只是引用,這個引用直接指向a指向的空間,而a指向的是一個列表空間

函式內部b=a看起來是賦值,賦值都是引用,sort函式直接是對b這個引用進行操作,但是b本身只是一個標籤,所以操作物件又去找b指向的地方,找到了a,a在函式內部實際上是傳入的引數,從外面傳入的本身就是一個引用,引用是沒有sort方法的,因此又繼續找,好了,a這個引用指向的是一個列表空間,那就對了,列表空間存在sort方法,因此直接使用sort進行對列表空間的排序,而sort方法本身是直接就地修改,這就很坑,相當於直接對這個指向的空間進行修改,這個空間上的引用實際上不止是函式中的引數在引用,函式外的a變數也在引用

那麼a在函式執行完成之後還是指向的這個列表空間,好了,瞬間GG,雖然外面a沒有變化,實際上空間內的資料已經發生變動,打印出來的a也會呈現新的表現形式