[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
建立一個list對象對象
建立一個名字內存
綁定string
再來看個例子:hash
s1 = "hello" s2 = "hello" print(s1 is s2)
這個會輸出什麼?True
! 由於是這樣的:
建立一個string對象
建立名字s1
並綁定到string對象
建立名字s2
並綁定到string對象
而s1
和s2
綁定的是同一個對象!
漲姿式了。那麼問題又來了:
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
而後就返回了。
至此真相大白。