跳转到内容

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已被完全复制。

所以,每当我使用函数或=时,我是否都要担心引用?好消息是,您只需要在使用字典和列表时担心引用。数字和字符串在赋值时会创建引用,但对数字和字符串的任何修改操作都会创建一个新的副本,因此您永远不会意外修改它们。在修改列表或字典时,您确实需要考虑引用。

现在您可能想知道为什么使用引用?基本原因是速度。为一个包含一千个元素的列表创建一个引用比复制所有元素要快得多。另一个原因是,它允许您有一个函数来修改输入的列表或字典。只要记住引用,如果您遇到数据在不应该改变时发生改变的奇怪问题,就可以了。

华夏公益教科书