免責聲明:我不使用Python,所以有些事情我說的可能是錯的。 Python專家,隨時糾正我。
偉大的問題。我認爲中央誤解(如果我甚至不能稱呼它,它是完全合理的,你如何到達你所使用的思維過程)您遇到提示你要問的問題是這樣的:
當我寫b[0] = a
時,並不意味着a
是在b
。這意味着b
包含一個引用,指向a
指向的內容。
變量a
和b
自己本身甚至不是「事物」,它們本身也僅僅是指向內存中其他匿名「事物」的指針。
引用的概念是從非編程世界的一次重大飛躍,讓我們通過你的程序與此步驟記:
>>> a = [0]
您創建碰巧有什麼事情列表(忽略那現在)。重要的是它是一個列表。該列表被存儲在內存中。假設它存儲在內存位置1001.然後,分配=
創建一個變量a
,該編程語言允許您稍後使用。此時,在內存中有一些列表對象,並且可以使用名稱a
訪問它。
>>> b = [0]
這對b
做了同樣的事情。有一個新的列表存儲在內存位置1002.編程語言創建一個引用b
,您可以用它來引用內存位置,並依次引用列表對象。
>>> a[0], b[0] = b, a
這做了兩件事是相同的,所以讓我們關注一個:a[0] = b
。這樣做非常花哨。它首先評估等式的右側,看到變量b
並獲取內存中的相應對象(內存對象#1002),因爲b
是對它的引用。左側發生的事情同樣花哨。 a
是一個指向列表(內存對象#1001)的變量,但是內存對象#1001本身具有多個自己的引用。這些參考文獻的數值不是像a
和b
這樣的名稱,而是具有數字索引,如0
。所以,現在,這是a
拉起內存對象#1001,這是一堆索引引用,並且它轉到索引爲0的引用(以前,此引用指向實際數字0
,這是你做的事情在第1行中),然後將該引用(即內存對象#1001中的第一個引用和唯一引用)重新指定爲方程右側的內容評估結果。所以現在,對象#1001的第0個參考點指向對象#1002。
>>> a
[[[...]]]
>>> b
[[[...]]]
這只是編程語言完成的幻想。當你只是要求它評估a
時,它會拉起內存對象(位置#1001處的列表),使用它自己的魔法檢測它是無限的,並將它自己渲染成這樣。
>>> a == b
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
RuntimeError: maximum recursion depth exceeded in cmp
該聲明的失敗與Python的比較方式有關。當你將一個對象與自己進行比較時,它立即計算結果爲true。當你比較和對象到另一個對象時,它使用「魔術」來確定等式應該是真還是假。對於Python中的列表,它會查看每個列表中的每個項目,並檢查它們是否相等(依次使用項目自己的等式檢查方法)。所以,當你嘗試a == b
。它所做的是首先挖掘b(對象#1002)和一個(對象#1001),然後意識到它們在內存中是不同的東西,所以它的遞歸列表檢查器。它通過迭代兩個列表來完成。對象#1001有一個索引爲0的元素指向對象#1002。對象#1002有一個索引爲0的元素指向對象#1001。因此,如果程序#1001和#1002(#1001的唯一參考點)和#1001(#1002的唯一參考點)是否相同,則程序得出結論:如果對象#1001和#1002的所有引用都指向相同的東西,一樣的東西。這種平等檢查永遠不會停止。同樣的事情會發生在任何不停止的列表中。你可以做c = [0]; d = [0]; c[0] = d; d[0] = c
和a == c
會引發同樣的錯誤。
>>> a[0] == b
True
正如我在前一段中暗示的那樣,由於Python採用了快捷方式,因此立即解決了這個問題。它不需要比較列表內容,因爲a[0]
指向對象#1002和b
指向對象#1002。 Python檢測到它們在字面意義上是相同的(它們是相同的「事物」),甚至不檢查內容。
>>> a[0][0] == b
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
RuntimeError: maximum recursion depth exceeded in cmp
這又回到了作爲一個錯誤,因爲a[0][0]
最終指向對象#1001。身份檢查失敗,並回退遞歸內容檢查,永遠不會結束。
>>> a[0][0][0] == b
True
再次,a[0][0][0]
指向對象#1002一樣,b
。遞歸檢查被跳過,比較立即返回true。
更高級別的胡言亂語胡言亂語不直接關係到你的特定代碼片段:
- 由於所有有所指的其他對象的引用,即使有這似乎是「無限」的嵌套,由
a
(正如我所說的對象#1001)引用的對象和被稱爲b
(#1002)的對象在內存中都是相同的大小。而且這個尺寸實際上非常小,因爲它們都是指向其他存儲位置的列表。
- 另外值得一注意,在更短的「大手筆」的語言,比較
==
回報true
只有如果它們指向的內存對象是在這個意義上,這兩個引用指向內存中的相同點相同的兩個引用。 Java就是這樣的一個例子。已經出現在這種語言中的文體慣例是定義對象本身的方法/函數(對於Java,通常稱爲equals()
)來執行自定義相等性測試。 Python爲列表開箱即用。我不太瞭解Python,但至少在Ruby中,==
負載過重,因爲在執行someobject == otherobject
時,它實際上調用someobject
(可以覆蓋)的==
方法。從理論上講,讓someobject == otherobject
返回布爾值以外的內容沒有任何阻礙。
這真是一個有趣的功能。 – phimuemue
真棒問題。我非常喜歡Python的這個功能,儘管我從來沒有找到它的用處。如果有人能夠提出這個特性的實際應用,那將是非常好的。或者寫一個模塊來生成包含所有列表的列表:P – andronikus
@andronikus:http://xkcd.com/468/ –