第二章-迴圈和列表(Loops and Lists)

介紹兩個迴圈語句:for 迴圈和 while 迴圈;介紹一種儲存資料的物件(object):列表

2.1-While迴圈

用於重複執行若干條語句

  • 結構為:
while 迴圈條件:
    需迴圈的語句1
    需迴圈的語句2

其他語句
  • while 迴圈開始的標誌:冒號(:)
  • while 迴圈的迴圈體:相對 while 縮排一次的連續的若干行語句
  • while 迴圈結束的標誌:迴圈體後第一條與 while 平齊的語句(如上面的其他語句)
  • while 迴圈過程:當滿足迴圈條件時,執行迴圈體,直至迴圈條件不滿足
  • 例子:
print '------------------'     # 表格開頭
C = -20                        # C的初始值
dC = 5                         # 迴圈內C的增長值
while C <= 40:                 # 迴圈由一個迴圈條件開始
    F = (9.0/5)*C + 32         # 第一條迴圈語句
    print C, F                 # 第二條迴圈語句
    C = C + dC                 # 第三條迴圈語句
print '------------------'     # 表格結尾(在迴圈之後執行)
  • 其結果為:
------------------
-20 -4.0
-15 5.0
-10 14.0
-5 23.0
0 32.0
5 41.0
10 50.0
15 59.0
20 68.0
25 77.0
30 86.0
35 95.0
40 104.0
------------------
  • 其目的:在 -20 到 40 間,以 5 為差值,將所有的攝氏溫度轉化為華氏溫度,並將其打印出來
  • 其中:
    • C = C + dC
       其為賦值語句。C 原先所指的 int 物件,在該語句執行後,自動被銷燬;原因在於,該語句執行後,C 指向了新的物件(即等號右側兩式相加的和),導致原先的 int 物件沒有任何變數指向它。(Before this assignment, C was already bound to an int object, and this object is automatically destroyed when C is bound to a new object and there are no other names (variables) referring to this previous object (if you did not get this last point, just relax and continue reading!).)
    • 對這種型別的賦值語句有以下簡寫形式:
      C += dC     # 等價於 C = C + dC
      C -= dC     # 等價於 C = C - dC
      C *= dC     # 等價於 C = C*dC
      C /= dC     # 等價於 C = C/dC
  • 對於迴圈條件而言,根據判斷結果,其結果為 True 或 False 中的一個
    C == 40     # C 等於 40
    C != 40     # C 不等於 40
    C >= 40     # C 大於等於 40
    C <= 40     # C 小於等於 40
    C > 40      # C 大於 40
    C < 40      # C 小於 40

    類似的表示式稱為邏輯表示式或布林表示式(logical or boolean expressions)

  • 在邏輯表示式前加 not,會使其值相反
    C = 1
    C == 40     # 結果為True
    not C == 40 # 結果為False

    not C == 40 與 C != 40 等價,但更推薦後一種表達方式

  • 邏輯表示式可由 and 或 or 相連,如:
    while x > 0 and y <= 1:
        print x, y

    其中,and 相連的邏輯表示式結果均為 True,結果才為 True;or 相連的邏輯表示式結果任意為 True,結果即為 True;其他情況均為 False

  • 注意:
    • 當 and 或 or 連線的是運算式時,返回的是其中的一個運算結果:
      a 的結果 a and b a or b
      0 / False 結果為 a 結果為 b
      非0 / True 結果為 b 結果為 a
      例如,(5 + 1) or -1 的值是 6; (5 + 1) and -1 的值是 -1
    • 對於各種物件而言,只要數字不為 0 或者字串(string)、列表(list)、字典(dictionary)等不為空,結果均為Ture。例如:
      >>> s = 'some string'
      >>> bool(s)
      True
      >>> s = '' # 空字串
      >>> bool(s)
      False
      

2.2-列表

2.2.1

列表:將資料按一定順序儲存起來

  • 形如:
    C = [-20, -15, -10, -5, 0, 5, 10, 15, 20, 25, 30, 35, 40]

    其中,-20、 -15 等資料稱為列表 C 的元素;列表的元素可以是任意物件;元素可以通過列表的索引(index)進行訪問,索引從 0 開始計數,例如,C[2] 的返回值為 -10

  • 列表的一些重要操作:
    結構(Construction) 含義(Meaning)
    a = [] 初始化一個空列表
    a = [1, 4.4, 'run.py'] 初始化一個列表
    a.append(elem) 在列表的末尾新增一個元素 elem
    a + [1, 3] 兩個列表相加
    a.insert(i, e) 在當前索引為 i 的元素之前新增一個元素 e
    a[3] 索引為 3 的元素
    a[-1] 倒數第 1 個元素
    a[1:3] 切片(slice):從列表 a 索引為 1 的元素開始,到索引為 2 的所有元素,複製到一個子列表(sublist)中
    del a[3] 刪除索引為 3 的元素
    a.remove(e) 刪除值為 e 的元素
    a.index('run.py') 找到值為 'run.py' 的元素的索引
    'run.py' in a 檢查列表是否包含該元素
    a.count(v) 檢視值為 v 的元素的總個數
    len(a) 列表 a 包含的元素個數
    min(a) 列表 a 中,值最小的元素
    max(a) 列表 a 中,值最大的元素
    sum(a) 將列表 a 中的所有元素相加
    sorted(a) 返回順序排序的列表
    reversed(a) 返回倒序排序的列表
    b[3][0][2] 返回巢狀列表的元素
    isinstance(a, list) 如果 a 是列表,那麼返回 True
    type(a) is list 如果 a 是列表,那麼返回 True

    其中,巢狀列表是元素為列表的列表;形如 len(a) 的表示式稱為函式(function);在形如 a.append(elem) 的表示式中,稱 append 是列表 a 的一個方法(method)

  • 訪問列表元素有兩種方法:一種是通過索引來訪問,例如 C[2];另一種訪問方式依賴於列表的元素,例如:
    >>> somelist = ['book.tex', 'book.log', 'book.pdf']
    >>> texfile, logfile, pdf = somelist
    >>> texfile
    'book.tex'
    >>> logfile
    'book.log'
    >>> pdf
    'book.pdf'

    其中,第 2 行賦值語句左側變數的個數要和列表的元素個數相同,否則會報錯

2.2.2

for迴圈:遍歷列表中的每個元素,每讀取一個元素,進入一次迴圈體

  • 結構為:
for 元素 in 列表:
    迴圈體
  • for 迴圈開始的標誌、for 迴圈的迴圈體、for 迴圈結束的標誌的表示與 while 迴圈一致
  • for 迴圈過程:依次讀取列表中的每個元素,每讀取一個元素,進入一次迴圈體,直到所有元素被遍歷完畢
  • 例子:
degrees = [0, 10, 20, 40, 100]
for C in degrees:
    print 'list element:', C
print 'The degrees list has', len(degrees), 'elements'
  • 其結果為:
list element: 0
list element: 10
list element: 20
list element: 40
list element: 100
The degrees list has 5 elements
  • 其目的:依次讀取列表 degrees 的元素,並將其打印出來;最後輸出列表 degrees 的元素個數

2.3-列表和迴圈的選擇性使用(Alternative Implementations with Lists and Loops)

2.3.1

for 迴圈和 while 迴圈可相互轉化

  • 兩種等價的表達方式:
    # 用 for 迴圈表示
    for element in somelist:
        <process element>
    
    # 用 while 迴圈表示
    index = 0
    while index < len(somelist):
        element = somelist[index]
        <process element>
        index += 1

2.3.2

range 結構:按一定規則自動生成列表

  • range(n) 生成列表 [0, 1, 2, ..., n-1]
  • range(start, stop, step) 生成以 start 開頭、前後元素相差 step 、元素值在 start 與 stop 間但不包括 stop 的列表。例如,range(2, 8, 3) 返回 [2, 5],range(1, 11, 2) 返回 [1, 3, 5, 7, 9]
  • range(start, stop) 等價於 range(start, stop, 1)

2.3.3

for 迴圈遍歷列表時的兩種等價表達方式

  • 兩種等價的表達方式
    # 直接訪問
    for element in somelist:
        ...
    
    # 通過索引訪問
    for i in range(len(somelist)):
        element = somelist[i]
        ...

    推薦第一種表達方法,更為簡練(which is more elegant to read)

  • 生成列表時,還可以用 [i]*n 生成包含 n 個值為 i 的元素的列表;如 [0]*5 返回 [0, 0, 0, 0, 0]

2.3.4

修改列表元素時的注意事項

  • 試圖將每個元素均加上 5,可能會出現以下兩種方法:
    # 方法一
    for c in Cdegrees:
        c += 5
    
    # 方法二
    for i in range(len(Cdegrees)):
        Cdegrees[i] += 5

    方法一修改失敗,而方法二修改成功。

  • 方法一失敗的原因:在方法一中,c += 5 操作結果,使 c 指向了新的浮點數物件 (c + 5),即只修改了 c 的值,但是對列表沒有影響
  • 方法二成功的原因:在方法二中,通過索引的直接訪問,能直接修改列表的元素
  • 可呼叫enumerate函式,同時訪問索引和元素:
    for i, c in enumerate(Cdegrees):
        Cdegrees[i] = c + 5

    功能一致

2.3.5

列表推導(list comprehension):通過遍歷已有的列表,匯出新的列表

  • 結構:
    newlist = [E(e) for e in list]

    其中,E(e) 表示包含元素 e 的表示式

2.3.6

同時遍歷多個列表:兩種方法

  • # 方法一
    for i in range(len(Cdegrees)):
        print ’%5d %5.1f’ % (Cdegrees[i], Fdegrees[i])
    
    # 方法二
    for C, F in zip(Cdegrees, Fdegrees):
        print ’%5d %5.1f’ % (C, F)

    推薦方法二;其中,zip 函式使 n 個列表 (list1, list2, list3, ...) 變為一個以 n 元組(n-tuples)為元素的列表,每個 n 元組 (e1, e2, e3, ...) 中的 e1、e2、e3、... 表示 list1、list2、list3、... 對應的元素,當列表的所有元組均被訪問過後,for 迴圈停止

2.4-巢狀表(Nested Lists)

以列表為元素的列表

2.4.1

生成巢狀表

  • 生成巢狀表的幾種方法:
    # 方法一
    Cdegrees = range(-20, 41, 5) # -20, -15, ..., 35, 40
    Fdegrees = [(9.0/5)*C + 32 for C in Cdegrees]
    table1 = [Cdegrees, Fdegrees]
    
    # 方法二
    table2 = []
    for C, F in zip(Cdegrees, Fdegrees):
        table2.append([C, F])
    
    # 方法二的簡寫(利用列表推導)
    table2 = [[C, F] for C, F in zip(Cdegrees, Fdegrees)]
  • 兩種方法生成的巢狀表的結構分別如圖所示:
  • 巢狀表可通過索引直接訪問:例如,table1[0][3] 返回 35

2.4.2

列印巢狀表

  • 列印巢狀表的幾種方法:
    方法 特點
    print table

    輸出很長的一行結果

    import pprint

    pprint.pprint(table)

    結果過長的話,自動按元素換行

    import scitools.pprint2

    scitools.pprint2.pprint(table)

    可通過 scitools.pprint2.float_format 控制輸出格式,如通過 scitools.pprint2.float_format = ’%.2e’,使輸出結果均以保留兩位小數的科學計數法表示;預設的輸出格式為 '%g' 

    存在舍入誤差(round-off error)

    for C, F in table:
        print ’%5d %5.1f’ % (C, F)
    可自由地控制輸出格式

2.4.3

提取原有列表的一部分,形成新列表;該部分稱為子列表(sublist)或片段(slice)

  • 提取子列表的幾種方法:
    結構 含義
    A[i:] 提取 A 中 A[i] 之後(包括 A[i])的所有元素
    A[:j] 提取 A 中 A[j] 之前(不包括 A[j])的所有元素
    A[i:j] 提取 A 中 A[i] 之後(包括 A[i])、A[j] 之前(不包括 A[j])的所有元素
  • 子列表是原列表的一個拷貝(copy),對原列表的任意操作,均不影響已經生成了的子列表

2.4.4

遍歷巢狀表

  • 當巢狀表內,每個列表元素的元素個數均相等時,可用以下方法遍歷:
    for C, F in table:
        # 遍歷 C 和 F

    若個數不同,則遍歷次數由包含元素個數最少的列表元素決定

  • 通過索引遍歷
    for p in range(len(scores)):
        for g in range(len(scores[p])):
            score = scores[p][g]
            print ’%4d’ % score,
        print

    不大好,較難理解

  • 通過元素遍歷
    for player in scores:
        for game in player:
            print ’%4d’ % game,
        print

    很好,形象直觀

2.5-元組(Tuples)

可看做是一種不可修改的列表

  • 除了會修改元素的操作(比如:append,del,remove,index 和 sort)之外,列表的其他操作均適用於元組
  • 與列表相比,元組的優勢:
    • 可保護元素不被篡改
    • 基於元組的程式碼,比基於列表的程式碼更快
    • 元組在 Python 軟體中常被使用
    • 元組可作為字典(dictionaries)的鍵值(keys),而列表不能    (詳見第 6 章)

2.6-小結(Summary)

本章所學術語

  • 列表(list)
  • 元組(tuple)
  • 巢狀列表(和巢狀元組)(nested list (and nested tuple))
  • 子列表(子元組)(sublist (subtuple) or slice a[i:j])
  • while 迴圈(while loop)
  • for 迴圈(for loop)
  • 列表推導(list comprehension)
  • 布林表示式(boolean expression)