[英]Two variables seem to point to the same list, even though they should be unique
我在程序中使用列表,但我不理解以下行為。 我已經開始理解可變性以及它如何影響變量賦值,但我沒有看到這里的問題:
class Test:
def __init__(self, list_n):
list_a = list_n[:]
list_b = list_n[:]
print(list_a is list_b) # Prints False
print(list_a is list_n) # Prints False
print(list_b is list_n) # Prints False
list_a[0][0] = 1
print(list_a) # Both of these print [[1,0,0][0,0,0][0,0,0]]
print(list_b)
def main():
list_n = [[0,0,0],[0,0,0],[0,0,0]]
test = Test(list_n)
if __name__ == '__main__': main()
list_a
和list_b
似乎仍然指向同一個列表,即使我認為我采取了必要的措施來防止這種情況發生。
您的示例不起作用的原因是因為您只創建了list_n
的淺表副本 。 列表的淺表副本僅復制“頂級”列表。 淺復制列表不會復制子列表 。 列表的淺副本是通過調用list
上的copy()
( list.copy()
)或使用切片表示法( list[:]
)來創建的。
在C級別,當對作為列表的元素進行淺拷貝時,指向列表的指針(稱為PyObject
s)正從一個列表復制到另一個列表。 但是,不會復制每個子列表的實際指針,因此list_a
和list_b
都包含指向完全相同的子列表的指針。
說白了,你從來沒有在list_n
中list_n
每個子列表的list_n
,因此list_a
和list_b
仍然包含指向相同子列表的指針。 這可以通過創建list_n
“ list_n
復制”來list_n
- 無論嵌套級別如何,原始列表中每個子列表的副本 - 使用copy.deepcopy()
:
>>> from copy import deepcopy
>>>
>>> list_n = [[0,0,0],[0,0,0],[0,0,0]]
>>> list_a = deepcopy(list_n)
>>> list_b = deepcopy(list_n)
>>>
>>> list_a[0][0] = 1
>>> list_a
[[1, 0, 0], [0, 0, 0], [0, 0, 0]]
>>> list_b
[[0, 0, 0], [0, 0, 0], [0, 0, 0]]
>>>
deepcopy()
? 使用deepcopy()
的最大缺點之一是“深度復制”淺層嵌套列表需要花費大量時間。
如果您的列表只是淺層嵌套(深度為兩到三層),則應該只使用嵌套列表 deepcopy()
而不是deepcopy()
。 使用這種方法會更有效率(感謝@jonrsharpe指出這一點):
>>> list_n = [[0,0,0],[0,0,0],[0,0,0]]
>>> list_a = [x[:] for x in list_n]
>>> list_b = [x[:] for x in list_n]
使用此方法獲得的效率可以使用標准庫中的timeit
模塊觀察:
>>> import timeit
>>>
>>> setup="from copy import deepcopy; list_n = [[0,0,0],[0,0,0],[0,0,0]]"
>>> timeit.timeit(setup=setup, stmt="deepcopy(list_n)")
24.223977088928223
>>> timeit.timeit(setup=setup, stmt="[x[:] for x in list_n]")
1.2281990051269531
>>>
但是,如果您的列表更深,則應選擇使用deepcopy()
,即使它看起來有些笨重。 通常永遠不需要犧牲可讀性而不是效率。 此外,隨着列表理解變得越來越復雜, deepcopy()
復制deepcopy()
的效率開始變小。
deepcopy()
這么慢? deepcopy()
比大多數其他方法慢得多(感謝@Felix的問題),因為deepcopy()
比簡單的列表理解做了更多的工作。 與列表deecopy()
不同, deecopy()
必須在任意嵌套列表上工作,可能有deecopy()
嵌套。 因此,在淺嵌套列表中使用它是極端的過度殺傷,並且會導致執行時間慢得多。
為了更好地了解deepcopy()
在幕后的作用,您可以查看該函數的源代碼,因為它是開源的,可供公眾查看 。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.