跳轉到內容

Python 2.6 非程式設計師教程/列表進階

來自 Wikibooks,開放的書籍,開放的世界

我們已經瞭解了列表以及它們的使用方法。現在您已經有一些背景知識,我將更詳細地介紹列表。首先,我們將看看更多訪問列表中元素的方法,然後我們將討論如何複製它們。

以下是一些使用索引訪問列表中單個元素的示例

>>> some_numbers = ['zero', 'one', 'two', 'three', 'four', 'five']
>>> some_numbers[0]
'zero'
>>> some_numbers[4]
'four'
>>> some_numbers[5]
'five'

所有這些示例應該看起來都熟悉。如果您想要列表中的第一個專案,只需檢視索引 0。第二個專案是索引 1,依此類推,直到列表結束。但是,如果您想要列表中的最後一個專案怎麼辦?一種方法是使用len()函式,例如some_numbers[len(some_numbers) - 1]。這種方法有效,因為len()函式始終返回最後一個索引加 1。最後一個之前的專案將是some_numbers[len(some_numbers) - 2]。有一個更簡單的方法。在 Python 中,最後一個專案始終是索引 -1。倒數第二個是索引 -2,依此類推。以下是一些更多示例

>>> some_numbers[len(some_numbers) - 1]
'five'
>>> some_numbers[len(some_numbers) - 2]
'four'
>>> some_numbers[-1]
'five'
>>> some_numbers[-2]
'four'
>>> some_numbers[-6]
'zero'

因此,列表中的任何專案都可以透過兩種方式索引:從前面索引和從後面索引。

另一種有用的訪問列表部分的方法是使用切片。以下是一個示例,讓您瞭解它們的用途

>>> things = [0, 'Fred', 2, 'S.P.A.M.', 'Stocking', 42, "Jack", "Jill"]
>>> things[0]
0
>>> things[7]
'Jill'
>>> things[0:8]
[0, 'Fred', 2, 'S.P.A.M.', 'Stocking', 42, 'Jack', 'Jill']
>>> things[2:4]
[2, 'S.P.A.M.']
>>> things[4:7]
['Stocking', 42, 'Jack']
>>> things[1:5]
['Fred', 2, 'S.P.A.M.', 'Stocking']

切片用於返回列表的一部分。切片運算子的形式為things[first_index:last_index]。切片會在first_index之前和last_index之前切斷列表,並返回之間的部分。您可以使用兩種型別的索引

>>> things[-4:-2]
['Stocking', 42]
>>> things[-4]
'Stocking'
>>> things[-4:6]
['Stocking', 42]

切片另一個技巧是未指定的索引。如果第一個索引未指定,則假定為列表的開頭。如果最後一個索引未指定,則假定為列表的剩餘部分。以下是一些示例

>>> things[:2]
[0, 'Fred']
>>> things[-2:]
['Jack', 'Jill']
>>> things[:3]
[0, 'Fred', 2]
>>> things[:-5]
[0, 'Fred', 2]

這是一個(受 HTML 啟發)的程式示例(如果您想複製和貼上詩歌定義,請這樣做)

poem = ["<B>", "Jack", "and", "Jill", "</B>", "went", "up", "the",
        "hill", "to", "<B>", "fetch", "a", "pail", "of", "</B>",
        "water.", "Jack", "fell", "<B>", "down", "and", "broke",
        "</B>", "his", "crown", "and", "<B>", "Jill", "came",
        "</B>", "tumbling", "after"]

def get_bolds(text):
    true = 1
    false = 0
    ## is_bold tells whether or not we are currently looking at 
    ## a bold section of text.
    is_bold = false
    ## start_block is the index of the start of either an unbolded 
    ## segment of text or a bolded segment.
    start_block = 0
    for index in range(len(text)):
        ## Handle a starting of bold text
        if text[index] == "<B>":
            if is_bold:
                print "Error: Extra Bold"
            ## print "Not Bold:", text[start_block:index]
            is_bold = true
            start_block = index + 1
        ## Handle end of bold text
        ## Remember that the last number in a slice is the index 
        ## after the last index used.
        if text[index] == "</B>":
            if not is_bold:
                print "Error: Extra Close Bold"
            print "Bold [", start_block, ":", index, "]", text[start_block:index]
            is_bold = false
            start_block = index + 1

get_bolds(poem)

輸出為

Bold [ 1 : 4 ] ['Jack', 'and', 'Jill']
Bold [ 11 : 15 ] ['fetch', 'a', 'pail', 'of']
Bold [ 20 : 23 ] ['down', 'and', 'broke']
Bold [ 28 : 30 ] ['Jill', 'came']

get_bold()函式接受一個分成單詞和標記的列表。它尋找的標記是<B>,它表示粗體文字的開始,以及</B>,它表示粗體文字的結束。get_bold()函式會遍歷列表,並搜尋開始和結束標記。

列表的下一個功能是複製它們。如果您嘗試類似以下的簡單操作

>>> a = [1, 2, 3]
>>> b = a
>>> print b
[1, 2, 3]
>>> b[1] = 10
>>> print b
[1, 10, 3]
>>> print a
[1, 10, 3]

這可能看起來很奇怪,因為修改b導致a也發生了改變。發生的事情是,語句b = a 使b成為對a引用。這意味著b可以被認為是a的另一個名稱。因此,對b的任何修改也會改變a。但是,有些賦值不會為一個列表建立兩個名稱

>>> a = [1, 2, 3]
>>> b = a * 2
>>> print a
[1, 2, 3]
>>> print b
[1, 2, 3, 1, 2, 3]
>>> a[1] = 10
>>> print a
[1, 10, 3]
>>> print b
[1, 2, 3, 1, 2, 3]

在這種情況下,b不是對a的引用,因為表示式a * 2會建立一個新的列表。然後,語句b = a * 2 使b成為對a * 2的引用,而不是對a的引用。所有賦值操作都會建立一個引用。當您將列表作為引數傳遞給函式時,您也會建立一個引用。大多數情況下,您不必擔心建立引用而不是副本。但是,當您需要修改一個列表而不改變列表的另一個名稱時,您必須確保您確實建立了一個副本。

有幾種方法可以建立列表的副本。最簡單的方法,也是大多數情況下有效的,是切片運算子,因為它始終建立一個新列表,即使它是整個列表的切片

>>> a = [1, 2, 3]
>>> b = a[:]
>>> b[1] = 10
>>> print a
[1, 2, 3]
>>> print b
[1, 10, 3]

獲取切片[:]會建立列表的新副本。但是,它只複製外部列表。內部的任何子列表仍然是對原始列表中子列表的引用。因此,當列表包含列表時,必須複製內部列表。您可以手動完成此操作,但是 Python 已經包含一個模組來執行此操作。您可以使用copy模組的deepcopy函式

>>> import copy
>>> a = [[1, 2, 3], [4, 5, 6]]
>>> b = a[:]
>>> c = copy.deepcopy(a)
>>> b[0][1] = 10
>>> c[1][1] = 12
>>> print a
[[1, 10, 3], [4, 5, 6]]
>>> print b
[[1, 10, 3], [4, 5, 6]]
>>> print c
[[1, 2, 3], [4, 12, 6]]

首先請注意,a是一個列表的列表。然後注意,當執行b[0][1] = 10時,ab都會發生改變,但c不會。這是因為當使用切片運算子時,內部陣列仍然是引用。但是,使用deepcopyc已被完全複製。

所以,每當我使用函式或=時,我是否都要擔心引用?好訊息是,您只需要在使用字典和列表時擔心引用。數字和字串在賦值時會建立引用,但對數字和字串的任何修改操作都會建立一個新的副本,因此您永遠不會意外修改它們。在修改列表或字典時,您確實需要考慮引用。

現在您可能想知道為什麼使用引用?基本原因是速度。為一個包含一千個元素的列表建立一個引用比複製所有元素要快得多。另一個原因是,它允許您有一個函式來修改輸入的列表或字典。只要記住引用,如果您遇到資料在不應該改變時發生改變的奇怪問題,就可以了。

華夏公益教科書