一、賦值生成引用,而不是拷貝
首先你需要知道,Python中賦值操作總是儲存對象的引用,而不是對象的拷貝。例如:
a = 3
b = [3, a, 3]
請問,當(dāng)運行 a = 9 時
列表 b 是什么樣子?
答案:不變
由于引用實現(xiàn)為指針,b = [3, a, 3] 意思是:列表的第一、三個元素是3,中間的元素指向變量 a 所指向的對象3。因為數(shù)字、字符串、元組是不可變的,所以,當(dāng)運行到 a = 9 時,實際上是創(chuàng)建對象 9,再將已有的變量 a 指向?qū)ο?9的內(nèi)存空間。而對象 3 仍然在原來的內(nèi)存空間上,所以列表第二個元素也還指向它。
再比如:
a = [1,2,3]
b = [3, a, 3]
請問,當(dāng)運行a[1] = 99時,
b是什么樣子?
答案:[3, [1, 99, 3], 3]
我們知道,變量 a 引用了列表 [1,2,3]。同時,變量 b 中的第二個元素也引用了 [1,2,3],關(guān)鍵點是:a[1] = 99運行后,是在原處改變了列表 [1,2,3],變?yōu)?[1, 99, 3],而不是重新創(chuàng)建新列表,因為列表與數(shù)字的區(qū)別之一是,它可以完全的自由改變,而數(shù)字不可變,也就是說不可在原處改變。
在這里,列表 b 引用了列表 a所指向的對象,因此,改變列表 a 指向的對象內(nèi)容時,也改變了列表 b 所指向的內(nèi)容,這稱為共享引用。
如果你不希望有這樣的特性,可以明確地對它們進(jìn)行拷貝,以避免對象的共享。就列表而言,可以通過沒有限制條件的分片,生成一個新的拷貝,也就是通過 list[:] 產(chǎn)生一個新列表:
a = [1,2,3]
b = [3, a[:], 3]
注意,當(dāng)列表分片中,起始索引和結(jié)束索引都省略的情況下,分片就會抽取序列中的每一項,這樣就生成了一個頂部拷貝(一個新的、無共享的對象)。