Python 建立由列表組成的列表

    列表是容器序列,裏面存放的是對象的引用,使用下面的方法創建的列表賦值時會有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 不可變對象更新值時 創建新的對象 因此不再時同樣的對象

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章