1. 程式人生 > 程式設計 >Python列表巢狀常見坑點及解決方案

Python列表巢狀常見坑點及解決方案

1.巢狀列表

Python中有一種內建的資料型別叫列表(list),它是一種容器,可以用來承載其他的物件(準確的說是其他物件的引用),列表中的物件可以稱為列表的元素,很明顯我們可以把列表作為列表中的元素,這就是所謂的巢狀列表。

巢狀列表可以模擬出現實中的表格、矩陣、2D遊戲的地圖(如植物大戰殭屍的花園)、棋盤(如國際象棋、黑白棋)等。

2.識別坑點

在使用巢狀的列表時要小心,否則很可能遭遇非常尷尬的情況,下面是一個小例子。

def main():  names = ['關羽','張飛','趙雲','馬超','黃忠']  subjs = ['語文','數學','英語']  scores = [[0] * 3] * 5  for row,name in enumerate(names):    print('請輸入%s的成績' % name)    for col,subj in enumerate(subjs):      scores[row][col] = float(input(subj + ': '))  print(scores)if __name__ == '__main__':  main()
  names = ['關羽','黃忠']
  subjs = ['語文','英語']
  scores = [[0] * 3] * 5
  for row,name in enumerate(names):
    print('請輸入%s的成績' % name)
    for col,subj in enumerate(subjs):
      scores[row][col] = float(input(subj + ': '))
  print(scores)
if __name__ == '__main__':
  main()

我們希望錄入5個學生3門課程的成績,於是定義了一個有5個元素的列表,而列表中的每個元素又是一個由3個元素構成的列表,這樣一個列表的列表剛好跟一個表格是一致的,相當於有5行3列。

接下來我們通過巢狀的for-in迴圈輸入每個學生3門課程的成績。程式執行完成後我們發現,每個學生3門課程的成績是一模一樣的(尷尬),而且就是最後錄入的那個學生的成績。

3。區分兩個概念

要想把這個坑填平,我們首先要區分物件和物件的引用這兩個概念,而要區分這兩個概念,還得先說說記憶體中的棧和堆。

我們經常會聽人說起“堆疊”這個詞,但實際上“堆”和“棧”是兩個不同的概念。眾所周知,一個程式執行時需要佔用一些記憶體空間來儲存資料和程式碼,那麼這些記憶體從邏輯上又可以做進一步的劃分。

對底層語言(如C語言)有所瞭解的程式設計師大都知道,程式中可以使用的記憶體從邏輯上可以為五個部分,按照地址從高到低依次是:棧(stack)、堆(heap)、資料段(data segment)、只讀資料段(static area)和程式碼段(code segment)。

棧用來儲存區域性、臨時變數,以及函式呼叫時儲存現場和恢復現場需要用到的資料,這部分記憶體在程式碼塊開始執行時自動分配,程式碼塊執行結束時自動釋放,通常由編譯器自動管理。

堆的大小不固定,可以動態的分配和回收,因此如果程式中有大量的資料需要處理,這些資料通常都放在堆上,如果堆空間沒有正確的被釋放會引發記憶體洩露的問題,而像Python、Java等程式語言都使用了垃圾回收機制來實現自動化的記憶體管理(自動回收不再使用的堆空間)。

4。小例子

所以,下面的程式碼中,變數a並不是真正的物件,它是物件的引用,相當於記錄了物件在堆空間的地址,通過這個地址我們可以訪問到對應的物件。

a = object()b = ['apple','pitaya','grape']
b = ['apple','grape']

同理,變數b是列表容器的引用,它引用了堆空間上的列表容器,而列表容器中並沒有儲存真正的物件,它儲存的也僅僅是物件的引用。

知道了這一點,我們可以回過頭看看剛才的程式,我們對列表進行[[0]* 3] * 5操作時,僅僅是將[0,0] 這個列表的地址進行了複製,並沒有建立新的列表物件。

所以,容器中雖然有5個元素,但是這5個元素引用了同一個列表物件。這一點可以通過id函式檢查scores[0]和scores[1]的地址得到證實。在此我們舉一個小例子,讀者朋友們可以敲一敲加深印象。

a = [[0]*3]*5id(a[0])id(a[1])# id相等
id(a[1])
# id相等

5。正確程式碼

所以,正確的程式碼應該按照如下的方式進行修改。

def main():  names = ['關羽','英語']  scores = [[]] * 5  for row,name in enumerate(names):    print('請輸入%s的成績' % name)    scores[row] = [0] * 3 #變為不再巢狀    for col,subj in enumerate(subjs):      scores[row][col] = float(input(subj + ': '))  print(scores)if __name__ == '__main__':  main()'關羽','英語']
  scores = [[]] * 5
  for row,name in enumerate(names):
    print('請輸入%s的成績' % name)
    scores[row] = [0] * 3 #變為不再巢狀
    for col,subj in enumerate(subjs):
      scores[row][col] = float(input(subj + ': '))
  print(scores)
if __name__ == '__main__':
  main()

或者

def main():  names = ['關羽','英語']  scores = [[0] * 3 for _ in range(5)]  for row,name in enumerate(names):    print('請輸入%s的成績' % name)    scores[row] = [0] * 3    for col,'英語']
  scores = [[0] * 3 for _ in range(5)]
  for row,name in enumerate(names):
    print('請輸入%s的成績' % name)
    scores[row] = [0] * 3
    for col,subj in enumerate(subjs):
      scores[row][col] = float(input(subj + ': '))
  print(scores)
if __name__ == '__main__':
  main()

以上就是使用巢狀列表需要注意的問題及解決措施,希望大家多多總結,以此避免在使用巢狀列表或者複製物件時可能遇到的坑。

以上就是本文的全部內容,希望對大家的學習有所幫助,也希望大家多多支援我們。