列表是容器序列,裏面存放的是對象的引用,使用下面的方法創建的列表賦值時會有bug。
a = [['_'] * 3] * 3 # 想創建一個二維列表
print(a) # [['_', '_', '_'], ['_', '_', '_'], ['_', '_', '_']]
a[0][0] = '996' # 只改變第一個子列表的第一個值
print(a) # [['996', '_', '_'], ['996', '_', '_'], ['996', '_', '_']] 所有子列表的第一個值都變了
究其原因,是我們創建列表的時候外層的*3把裏面的['_'] * 3形成的列表對象的引用複製了同樣的三份。這就相當於創建一個列表['_', '_', '_'],並且把這個列表對象的引用反覆加了三次進去,因此一個改變另外幾個也改變。同下面的例子:
a = ['_'] * 3
print(a) # ['_', '_', '_']
b = []
for i in range(3):
b.append(a) # 反覆加了3次列表a的對象的引用
print(b) # [['_', '_', '_'], ['_', '_', '_'], ['_', '_', '_']]
b[0][0] = '996'
print(a) # ['996', '_', '_'] 對b操作 發現a也改變了
print(b) # [['996', '_', '_'], ['996', '_', '_'], ['996', '_', '_']] b中改了三處
要避免這個問題,最好的做法就是,讓3次加進去的列表對象不同,也就是創建3個內容一模一樣的列表依次加進去。如下面的做法:
b = []
for i in range(3):
a = ['_'] * 3 # 每次循環就創建一個新的列表a
b.append(a) # 每次循環加入一個新的列表
print(b) # [['_', '_', '_'], ['_', '_', '_'], ['_', '_', '_']]
b[0][0] = '996' # 改其中一個子列表的第一個值
print(b) # [['996', '_', '_'], ['_', '_', '_'], ['_', '_', '_']] 列表內的三個列表屬於不同的對象
這樣做看起來好像有點麻煩,也有更簡潔的方法:
a = [['_'] * 3 for i in range(3)] # 循環3次 每次創建一個新的['_'] * 3的列表加進去
print(a) # [['_', '_', '_'], ['_', '_', '_'], ['_', '_', '_']]
a[0][0] = '996'
print(a) # [['996', '_', '_'], ['_', '_', '_'], ['_', '_', '_']] 子列表的對象不同 因此互不影響
至於爲什麼['_'] * 3產生的列表['_', '_', '_']改變其中一個'_',另外兩個不變,是因爲字符串類型是不可變對象,一旦需要改變就要另外開闢空間,產生新的對象,所以他們之間更新值並無影響。例子如下:
a = ['_'] * 3
print(a) # ['_', '_', '_']
print(a[0] is a[1]) # True 剛開始沒改變值的時候 對象的引用相同
a[0] = '996' # 改變值
print(a) # ['996', '_', '_']
print(a[0] is a[1]) # False 不可變對象更新值時 創建新的對象 因此不再時同樣的對象