Python: 今天你賦值了嗎?

[EDIT] 沒想到會被轉發。很高興也很囧。看來眼球多了的確容易糾錯。要當心啊當心。編程

編程語言就是這樣。有時候你已經用它寫了數萬行代碼,自覺得很熟了。知道某一天遇到一個意料以外的問題。而後你才發現,原來TMD是這樣的。app

問題

def change(l1, l2):
    l1.append(10)
    l2 = [7, 5, 3, 1]


list1 = [1, 3, 5, 7]
list2 = [3, 3, 3, 3]
change(list1, list2)
print(list1)
print(list2)

以上這段代碼的輸出是什麼?若是你的答案是編程語言

[1, 3, 5, 7, 10]
[3, 3, 3, 3]

那麼大牛請安靜的離開。若是不是,能夠讀一下下面的故事。函數

賦值

最簡單的賦值ui

list1 = [1, 3, 5, 7]

到底發生了什麼呢?一個變量list1被賦值了?太籠統了吧。
對於Python來講,Everything is an object。上面這行代碼實際上是這樣的:code

  1. 建立一個list對象對象

  2. 建立一個名字內存

  3. 綁定string

再來看個例子:hash

s1 = "hello"
s2 = "hello"
print(s1 is s2)

這個會輸出什麼?True! 由於是這樣的:

  1. 建立一個string對象

  2. 建立名字s1並綁定到string對象

  3. 建立名字s2並綁定到string對象

s1s2綁定的是同一個對象!

漲姿式了。那麼問題又來了:

l1 = [1, 3, 5, 7]
l2 = [1, 3, 5, 7]
print(l1 is l2)

結果是?。。。。。。必定是True了吧?錯!

這裏又有一個很重要的概念。Mutable 和 immutable。"hello"[1, 3, 5, 7]的區別就在於:前者是Immutable然後者是Mutable。對於可變的對象,Python會自動生成全新的對象,以確保各對象能夠獨立的變更。而不可變的就不須要 - 節省內存。

傳參

回到開始的問題。當list1被傳給change()的時候,究竟是什麼被傳過去了?讓咱們來作一個小實驗。在傳入前和change()內部各加入一個語句

print(id(list1))

print(id(l1))

這裏要解釋一下,Python的每個對象都有一個惟一的hash id。id()這個函數就是用來打印出這個id的。加了上面兩行以後,咱們會發現。在change()函數以外與以內,兩個值是同樣的。也就是說Python是把同一個對象傳給了change()

這下咱們就能夠理解爲何list1在調用了change()以後被改動了。可是那麼list2也應該被改動了纔對啊?非也。這裏,list2所綁定的對象的確被傳進來了,的確生成了一個新list對象。可是這個對象被綁定給了l2。跟list2沒半毛錢關係。咱們在作一個實驗驗證一下。

def change(l1, l2):
    l1.append(10)
    l2 = [7, 5, 3, 1]


list1 = [1, 3, 5, 7]
list2 = [3, 3, 3, 3]
change(list1, list2)
print(list1)
print(list2)

import dis
dis.dis(change)

對比開始的代碼,咱們加入了最後兩句。這個兩句使用了dis模塊把change反編譯了。結果以下:

2 0 LOAD_FAST 0 (l1)

3 LOAD_ATTR                0 (append)
          6 LOAD_CONST               1 (10)
          9 CALL_FUNCTION            1
         12 POP_TOP

3 13 LOAD_CONST 2 (7)

16 LOAD_CONST               3 (5)
         19 LOAD_CONST               4 (3)
         22 LOAD_CONST               5 (1)
         25 BUILD_LIST               4
         28 STORE_FAST               1 (l2)
         31 LOAD_CONST               0 (None)
         34 RETURN_VALUE

對應原來的行號3,咱們能夠看到,Python用4個常數build了一個list。而後把這個綁定到了名字l2而後就返回了。

至此真相大白。

相關文章
相關標籤/搜索