geekdoc-python-zh/docs/realpython/python-lists-tuples.md

29 KiB
Raw Blame History

Python 中的列表和元组

原文:https://realpython.com/python-lists-tuples/

*立即观看**本教程有真实 Python 团队创建的相关视频课程。和写好的教程一起看,加深理解:Python 中的列表和元组

列表元组可以说是 Python 最通用、最有用的数据类型。几乎在每一个重要的 Python 程序中都可以找到它们。

在本教程中,您将学到以下内容:您将了解列表和元组的重要特征。您将学习如何定义它们以及如何操作它们。完成后,您应该对在 Python 程序中何时以及如何使用这些对象类型有了很好的感觉。

***参加测验:***通过我们的交互式“Python 列表和元组”测验来测试您的知识。完成后,您将收到一个分数,以便您可以跟踪一段时间内的学习进度:

*参加测验

Python 列表

简而言之,列表是任意对象的集合,有点类似于许多其他编程语言中的数组,但是更加灵活。在 Python 中,通过将逗号分隔的对象序列放在方括号([])中来定义列表,如下所示:

>>> a = ['foo', 'bar', 'baz', 'qux']

>>> print(a)
['foo', 'bar', 'baz', 'qux']
>>> a
['foo', 'bar', 'baz', 'qux']

Python 列表的重要特征如下:

  • 列表是有序的。
  • 列表可以包含任意对象。
  • 列表元素可以通过索引来访问。
  • 列表可以嵌套到任意深度。
  • 列表是可变的。
  • 列表是动态的。

下面将更详细地研究这些特性。

Remove ads

列表已排序

列表不仅仅是对象的集合。它是对象的有序集合。定义列表时指定元素的顺序是该列表的固有特征,并在该列表的生存期内保持不变。(在下一个字典教程中,您将看到一个无序的 Python 数据类型。)

具有不同顺序的相同元素的列表是不同的:

>>> a = ['foo', 'bar', 'baz', 'qux']
>>> b = ['baz', 'qux', 'bar', 'foo']
>>> a == b
False
>>> a is b
False

>>> [1, 2, 3, 4] == [4, 1, 3, 2]
False

列表可以包含任意对象

列表可以包含任何种类的对象。列表的元素可以都是相同的类型:

>>> a = [2, 4, 6, 8]
>>> a
[2, 4, 6, 8]

或者元素可以是不同的类型:

>>> a = [21.42, 'foobar', 3, 4, 'bark', False, 3.14159]
>>> a
[21.42, 'foobar', 3, 4, 'bark', False, 3.14159]

列表甚至可以包含复杂的对象,如函数、类和模块,您将在接下来的教程中了解这些内容:

>>> int
<class 'int'>
>>> len
<built-in function len>
>>> def foo():
...     pass
...
>>> foo
<function foo at 0x035B9030>
>>> import math
>>> math
<module 'math' (built-in)>

>>> a = [int, len, foo, math]
>>> a
[<class 'int'>, <built-in function len>, <function foo at 0x02CA2618>,
<module 'math' (built-in)>]

列表可以包含任意数量的对象,从零到计算机内存允许的数量:

>>> a = []
>>> a
[]

>>> a = [ 'foo' ]
>>> a
['foo']

>>> a = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20,
... 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40,
... 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60,
... 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80,
... 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100]
>>> a
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20,
21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39,
40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58,
59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77,
78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96,
97, 98, 99, 100]

(只有一个对象的列表有时被称为单例列表。)

列表对象不必是唯一的。给定对象可以在列表中出现多次:

>>> a = ['bark', 'meow', 'woof', 'bark', 'cheep', 'bark']
>>> a
['bark', 'meow', 'woof', 'bark', 'cheep', 'bark']

列表元素可以通过索引来访问

列表中的单个元素可以使用方括号中的索引来访问。这完全类似于访问字符串中的单个字符。列表索引和字符串一样,是从零开始的。

考虑以下列表:

>>> a = ['foo', 'bar', 'baz', 'qux', 'quux', 'corge']

a中元素的索引如下所示:

Diagram of a Python list

List Indices

下面是访问a的一些元素的 Python 代码:

>>> a[0]
'foo'
>>> a[2]
'baz'
>>> a[5]
'corge'

几乎所有关于字符串索引的工作都类似于列表。例如,负列表索引从列表末尾开始计数:

Diagram of a Python list

Negative List Indexing
>>> a[-1]
'corge'
>>> a[-2]
'quux'
>>> a[-5]
'bar'

切片也可以。如果a是一个列表,表达式a[m:n]返回从索引m到索引na部分,但不包括索引【】:

>>> a = ['foo', 'bar', 'baz', 'qux', 'quux', 'corge']

>>> a[2:5]
['baz', 'qux', 'quux']

字符串切片的其他特性同样适用于列表切片:

  • 可以指定正索引和负索引:

    >>> a[-5:-2]
    ['bar', 'baz', 'qux']
    >>> a[1:4]
    ['bar', 'baz', 'qux']
    >>> a[-5:-2] == a[1:4]
    True` 
    
  • 省略第一个索引会从列表的开头开始切片,省略第二个索引会将切片扩展到列表的结尾:

    >>> print(a[:4], a[0:4])
    ['foo', 'bar', 'baz', 'qux'] ['foo', 'bar', 'baz', 'qux']
    >>> print(a[2:], a[2:len(a)])
    ['baz', 'qux', 'quux', 'corge'] ['baz', 'qux', 'quux', 'corge']
    
    >>> a[:4] + a[4:]
    ['foo', 'bar', 'baz', 'qux', 'quux', 'corge']
    >>> a[:4] + a[4:] == a
    True` 
    
  • 您可以指定步幅,可以是正的,也可以是负的:

    >>> a[0:6:2]
    ['foo', 'baz', 'quux']
    >>> a[1:6:2]
    ['bar', 'qux', 'corge']
    >>> a[6:0:-2]
    ['corge', 'qux', 'bar']` 
    
  • 反转列表的语法与反转字符串的语法相同:

    >>> a[::-1]
    ['corge', 'quux', 'qux', 'baz', 'bar', 'foo']` 
    
  • [:]语法适用于列表。但是,这个操作如何处理列表和如何处理字符串之间有一个重要的区别。

    如果s是一个字符串,s[:]返回对同一对象的引用:

    >>> s = 'foobar'
    >>> s[:]
    'foobar'
    >>> s[:] is s
    True` 
    

    相反,如果a是一个列表,a[:]返回一个新对象,它是a的副本:

    >>> a = ['foo', 'bar', 'baz', 'qux', 'quux', 'corge']
    >>> a[:]
    ['foo', 'bar', 'baz', 'qux', 'quux', 'corge']
    >>> a[:] is a
    False` 
    

几个 Python 运算符和内置函数也可以以类似于字符串的方式用于列表:

  • innot in操作符:

    >>> a
    ['foo', 'bar', 'baz', 'qux', 'quux', 'corge']
    
    >>> 'qux' in a
    True
    >>> 'thud' not in a
    True` 
    
  • 串联(+)和复制(*)运算符:

    >>> a
    ['foo', 'bar', 'baz', 'qux', 'quux', 'corge']
    
    >>> a + ['grault', 'garply']
    ['foo', 'bar', 'baz', 'qux', 'quux', 'corge', 'grault', 'garply']
    >>> a * 2
    ['foo', 'bar', 'baz', 'qux', 'quux', 'corge', 'foo', 'bar', 'baz',
    'qux', 'quux', 'corge']` 
    
  • len()min()max() 功能:

    >>> a
    ['foo', 'bar', 'baz', 'qux', 'quux', 'corge']
    
    >>> len(a)
    6
    >>> min(a)
    'bar'
    >>> max(a)
    'qux'` 
    

字符串和列表的行为如此相似并不是偶然的。它们都是被称为 iterable 的更一般的对象类型的特例,您将在接下来的关于确定迭代的教程中更详细地遇到它。

顺便说一下,在上面的每个例子中,在对其执行操作之前,列表总是被分配给一个变量。但是您也可以对列表文字进行操作:

>>> ['foo', 'bar', 'baz', 'qux', 'quux', 'corge'][2]
'baz'

>>> ['foo', 'bar', 'baz', 'qux', 'quux', 'corge'][::-1]
['corge', 'quux', 'qux', 'baz', 'bar', 'foo']

>>> 'quux' in ['foo', 'bar', 'baz', 'qux', 'quux', 'corge']
True

>>> ['foo', 'bar', 'baz'] + ['qux', 'quux', 'corge']
['foo', 'bar', 'baz', 'qux', 'quux', 'corge']

>>> len(['foo', 'bar', 'baz', 'qux', 'quux', 'corge'][::-1])
6

就此而言,您可以对字符串文字做同样的事情:

>>> 'If Comrade Napoleon says it, it must be right.'[::-1]
'.thgir eb tsum ti ,ti syas noelopaN edarmoC fI'

Remove ads

列表可以嵌套

您已经看到列表中的元素可以是任何类型的对象。这包括另一份名单。一个列表可以包含子列表,子列表又可以包含子列表本身,依此类推,直到任意深度。

考虑这个(公认是人为的)例子:

>>> x = ['a', ['bb', ['ccc', 'ddd'], 'ee', 'ff'], 'g', ['hh', 'ii'], 'j']
>>> x
['a', ['bb', ['ccc', 'ddd'], 'ee', 'ff'], 'g', ['hh', 'ii'], 'j']

x引用的对象结构如下图所示:

Nested lists diagram

A Nested List

x[0]x[2]x[4]是字符串,每个都是一个字符长:

>>> print(x[0], x[2], x[4])
a g j

但是x[1]x[3]是子列表:

>>> x[1]
['bb', ['ccc', 'ddd'], 'ee', 'ff']

>>> x[3]
['hh', 'ii']

要访问子列表中的项目,只需附加一个额外的索引:

>>> x[1]
['bb', ['ccc', 'ddd'], 'ee', 'ff']

>>> x[1][0]
'bb'
>>> x[1][1]
['ccc', 'ddd']
>>> x[1][2]
'ee'
>>> x[1][3]
'ff'

>>> x[3]
['hh', 'ii']
>>> print(x[3][0], x[3][1])
hh ii

x[1][1]是另一个子列表,所以再添加一个索引就可以访问它的元素:

>>> x[1][1]
['ccc', 'ddd']
>>> print(x[1][1][0], x[1][1][1])
ccc ddd

用这种方式嵌套列表的深度和复杂度没有限制,除非你的计算机内存不够大。

所有关于索引和切片的常用语法也适用于子列表:

>>> x[1][1][-1]
'ddd'
>>> x[1][1:3]
[['ccc', 'ddd'], 'ee']
>>> x[3][::-1]
['ii', 'hh']

然而,要注意操作符和函数只适用于你指定的层次上的列表,而不是递归。考虑当您使用len()查询x的长度时会发生什么:

>>> x
['a', ['bb', ['ccc', 'ddd'], 'ee', 'ff'], 'g', ['hh', 'ii'], 'j']
>>> len(x)
5

>>> x[0]
'a'
>>> x[1]
['bb', ['ccc', 'ddd'], 'ee', 'ff']
>>> x[2]
'g'
>>> x[3]
['hh', 'ii']
>>> x[4]
'j'

只有五个元素——三个字符串和两个子列表。子列表中的单个元素不计入x的长度。

使用in操作符时,您会遇到类似的情况:

>>> 'ddd' in x
False
>>> 'ddd' in x[1]
False
>>> 'ddd' in x[1][1]
True

'ddd'不是xx[1]中的元素之一。它只是子列表x[1][1]中的一个直接元素。子列表中的单个元素不能算作父列表中的元素。

Remove ads

列表是可变的

到目前为止,您遇到的大多数数据类型都是原子类型。例如,整数或浮点对象是不能再进一步分解的基本单元。这些类型是不可变的,这意味着它们一旦被赋值就不能被改变。想改变一个整数的值没有太大意义。如果你想要一个不同的整数,你只需要分配一个不同的。

相比之下,字符串类型是一种复合类型。字符串可以简化为更小的部分,即组成字符。考虑改变字符串中的字符可能是有意义的。但是你不能。在 Python 中,字符串也是不可变的。

列表是您遇到的第一个可变数据类型。一旦创建了列表就可以随意添加、删除、移动和移动元素。Python 提供了多种修改列表的方法。

修改单个列表值

列表中的单个值可以通过索引和简单赋值来替换:

>>> a = ['foo', 'bar', 'baz', 'qux', 'quux', 'corge']
>>> a
['foo', 'bar', 'baz', 'qux', 'quux', 'corge']

>>> a[2] = 10
>>> a[-1] = 20
>>> a
['foo', 'bar', 10, 'qux', 'quux', 20]

您可能还记得教程中的字符串和 Python 中的字符数据,您不能用字符串来做这件事:

>>> s = 'foobarbaz'
>>> s[2] = 'x'
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: 'str' object does not support item assignment

使用del命令可以删除列表项:

>>> a = ['foo', 'bar', 'baz', 'qux', 'quux', 'corge']

>>> del a[3]
>>> a
['foo', 'bar', 'baz', 'quux', 'corge']

修改多个列表值

如果你想一次改变一个列表中几个连续的元素怎么办Python 允许使用切片赋值来实现这一点,其语法如下:

a[m:n] = <iterable>

现在,再次把 iterable 看作一个列表。该赋值用<iterable>替换a的指定片:

>>> a = ['foo', 'bar', 'baz', 'qux', 'quux', 'corge']

>>> a[1:4]
['bar', 'baz', 'qux']
>>> a[1:4] = [1.1, 2.2, 3.3, 4.4, 5.5]
>>> a
['foo', 1.1, 2.2, 3.3, 4.4, 5.5, 'quux', 'corge']
>>> a[1:6]
[1.1, 2.2, 3.3, 4.4, 5.5]
>>> a[1:6] = ['Bark!']
>>> a
['foo', 'Bark!', 'quux', 'corge']

插入的元素数不必等于替换的元素数。Python 只是根据需要增加或缩小列表。

您可以插入多个元素来代替单个元素,只需使用仅表示一个元素的切片即可:

>>> a = [1, 2, 3]
>>> a[1:2] = [2.1, 2.2, 2.3]
>>> a
[1, 2.1, 2.2, 2.3, 3]

请注意,这不同于用列表替换单个元素:

>>> a = [1, 2, 3]
>>> a[1] = [2.1, 2.2, 2.3]
>>> a
[1, [2.1, 2.2, 2.3], 3]

您也可以在列表中插入元素,而不删除任何内容。只需在所需的索引处指定一个形式为[n:n](零长度切片)的切片:

>>> a = [1, 2, 7, 8]
>>> a[2:2] = [3, 4, 5, 6]
>>> a
[1, 2, 3, 4, 5, 6, 7, 8]

通过将适当的片分配给空列表,可以删除列表中间的多个元素。您也可以对同一个切片使用del语句:

>>> a = ['foo', 'bar', 'baz', 'qux', 'quux', 'corge']
>>> a[1:5] = []
>>> a
['foo', 'corge']

>>> a = ['foo', 'bar', 'baz', 'qux', 'quux', 'corge']
>>> del a[1:5]
>>> a
['foo', 'corge']

将项目添加到列表中

使用+串联运算符或+=增强赋值运算符,可以将附加项添加到列表的开头或结尾:

>>> a = ['foo', 'bar', 'baz', 'qux', 'quux', 'corge']

>>> a += ['grault', 'garply']
>>> a
['foo', 'bar', 'baz', 'qux', 'quux', 'corge', 'grault', 'garply']

>>> a = ['foo', 'bar', 'baz', 'qux', 'quux', 'corge']

>>> a = [10, 20] + a
>>> a
[10, 20, 'foo', 'bar', 'baz', 'qux', 'quux', 'corge']

请注意,一个列表必须与另一个列表连接在一起,因此如果您只想添加一个元素,则需要将其指定为单一列表:

>>> a = ['foo', 'bar', 'baz', 'qux', 'quux', 'corge']
>>> a += 20
Traceback (most recent call last):
  File "<pyshell#58>", line 1, in <module>
    a += 20
TypeError: 'int' object is not iterable

>>> a += [20]
>>> a
['foo', 'bar', 'baz', 'qux', 'quux', 'corge', 20]

注意:从技术上讲,说一个列表必须与另一个列表连接是不正确的。更准确地说,列表必须与可迭代的对象连接在一起。当然,列表是可迭代的,所以可以将一个列表与另一个列表连接起来。

字符串也是可迭代的。但是请注意当您将一个字符串连接到一个列表时会发生什么:

>>> a = ['foo', 'bar', 'baz', 'qux', 'quux']
>>> a += 'corge'
>>> a
['foo', 'bar', 'baz', 'qux', 'quux', 'c', 'o', 'r', 'g', 'e']

这个结果可能不太符合你的预期。当遍历一个字符串时,结果是其组成字符的列表。在上面的例子中,连接到列表a上的是字符串'corge'中的字符列表。

如果你真的想把单个字符串'corge'添加到列表的末尾,你需要把它指定为一个单例列表:

>>> a = ['foo', 'bar', 'baz', 'qux', 'quux']
>>> a += ['corge']
>>> a
['foo', 'bar', 'baz', 'qux', 'quux', 'corge']

如果这看起来很神秘,不要太担心。在关于明确迭代的教程中,您将了解到可迭代的来龙去脉。

修改列表的方法

最后Python 提供了几个可以用来修改列表的内置方法。这些方法的详细信息如下。

**注意:**你在上一个教程中看到的字符串方法并没有直接修改目标字符串。这是因为字符串是不可变的。相反,字符串方法返回一个新的字符串对象,该对象按照方法的指示进行修改。它们保持原来的目标字符串不变:

>>> s = 'foobar'
>>> t = s.upper()
>>> print(s, t)
foobar FOOBAR

列表方法不同。因为列表是可变的,所以这里显示的列表方法就地修改目标列表。

a.append(<obj>)

将对象追加到列表中。

a.append(<obj>) 将对象<obj>追加到列表a的末尾:

>>> a = ['a', 'b']
>>> a.append(123)
>>> a
['a', 'b', 123]

记住,列表方法就地修改目标列表。它们不会返回新的列表:

>>> a = ['a', 'b']
>>> x = a.append(123)
>>> print(x)
None
>>> a
['a', 'b', 123]

请记住,当使用+操作符连接一个列表时,如果目标操作数是可迭代的,那么它的元素将被分开并单独追加到列表中:

>>> a = ['a', 'b']
>>> a + [1, 2, 3]
['a', 'b', 1, 2, 3]

.append()方法不是那样工作的!如果用.append()将一个 iterable 追加到一个列表中,它将作为单个对象添加:

>>> a = ['a', 'b']
>>> a.append([1, 2, 3])
>>> a
['a', 'b', [1, 2, 3]]

因此,使用.append(),您可以将一个字符串作为单个实体追加:

>>> a = ['a', 'b']
>>> a.append('foo')
>>> a
['a', 'b', 'foo']

a.extend(<iterable>)

用 iterable 中的对象扩展列表。

是的,这大概就是你想的那样。.extend()也添加到列表的末尾,但是参数应该是可迭代的。<iterable>中的项目是单独添加的:

>>> a = ['a', 'b']
>>> a.extend([1, 2, 3])
>>> a
['a', 'b', 1, 2, 3]

换句话说,.extend()的行为类似于+操作符。更准确地说,因为它就地修改了列表,所以它的行为类似于+=操作符:

>>> a = ['a', 'b']
>>> a += [1, 2, 3]
>>> a
['a', 'b', 1, 2, 3]

a.insert(<index>, <obj>)

将对象插入列表。

a.insert(<index>, <obj>)在指定的<index>处将对象<obj>插入到列表a中。在方法调用之后,a[<index>]<obj>,剩余的列表元素被推到右边:

>>> a = ['foo', 'bar', 'baz', 'qux', 'quux', 'corge']
>>> a.insert(3, 3.14159)
>>> a[3]
3.14159
>>> a
['foo', 'bar', 'baz', 3.14159, 'qux', 'quux', 'corge']

a.remove(<obj>)

从列表中移除对象。

a.remove(<obj>)从列表a中删除对象<obj>。如果<obj>不在a中,则会引发一个异常:

>>> a = ['foo', 'bar', 'baz', 'qux', 'quux', 'corge']
>>> a.remove('baz')
>>> a
['foo', 'bar', 'qux', 'quux', 'corge']

>>> a.remove('Bark!')
Traceback (most recent call last):
  File "<pyshell#13>", line 1, in <module>
    a.remove('Bark!')
ValueError: list.remove(x): x not in list

a.pop(index=-1)

从列表中移除元素。

该方法与.remove()在两个方面不同:

  1. 您指定要移除的项目的索引,而不是对象本身。
  2. 该方法返回值:被移除的项。

a.pop()删除列表中的最后一项:

>>> a = ['foo', 'bar', 'baz', 'qux', 'quux', 'corge']

>>> a.pop()
'corge'
>>> a
['foo', 'bar', 'baz', 'qux', 'quux']

>>> a.pop()
'quux'
>>> a
['foo', 'bar', 'baz', 'qux']

如果指定了可选的<index>参数,则移除并返回该索引处的项目。<index>可能是负数,如字符串和列表索引:

>>> a = ['foo', 'bar', 'baz', 'qux', 'quux', 'corge']

>>> a.pop(1)
'bar'
>>> a
['foo', 'baz', 'qux', 'quux', 'corge']

>>> a.pop(-3)
'qux'
>>> a
['foo', 'baz', 'quux', 'corge']

<index>默认为-1,所以a.pop(-1)相当于a.pop()

Remove ads

列表是动态的

本教程从 Python 列表的六个定义特征开始。最后一点是列表是动态的。在上面的章节中,您已经看到了许多这样的例子。当项目添加到列表中时,它会根据需要增长:

>>> a = ['foo', 'bar', 'baz', 'qux', 'quux', 'corge']

>>> a[2:2] = [1, 2, 3]
>>> a += [3.14159]
>>> a
['foo', 'bar', 1, 2, 3, 'baz', 'qux', 'quux', 'corge', 3.14159]

类似地,列表会缩小以适应项目的移除:

>>> a = ['foo', 'bar', 'baz', 'qux', 'quux', 'corge']
>>> a[2:3] = []
>>> del a[0]
>>> a
['bar', 'qux', 'quux', 'corge']

python 元组

Python 提供了另一种类型,即有序的对象集合,称为元组。

发音因你问的人而异。有些人把它发音为“too-ple”(与“Mott the Hoople”押韵)其他人则发音为“tup-ple”(与“supple”押韵)。我倾向于后者,因为它可能与“五重”、“六重”、“八重”等等起源相同,而且我认识的每个人都把后者发音为与“柔软”押韵。

定义和使用元组

元组在所有方面都与列表相同,除了以下属性:

  • 元组是通过将元素括在圆括号(())而不是方括号([])中来定义的。
  • 元组是不可变的。

下面是一个简短的示例,展示了元组定义、索引和切片:

>>> t = ('foo', 'bar', 'baz', 'qux', 'quux', 'corge')
>>> t
('foo', 'bar', 'baz', 'qux', 'quux', 'corge')

>>> t[0]
'foo'
>>> t[-1]
'corge'
>>> t[1::2]
('bar', 'qux', 'corge')

不要害怕!我们最喜欢的字符串和列表反转机制也适用于元组:

>>> t[::-1]
('corge', 'quux', 'qux', 'baz', 'bar', 'foo')

**注意:**即使元组是用括号定义的,你还是要用方括号对元组进行索引和切片,就像对字符串和列表一样。

你所了解的关于列表的一切——它们是有序的,它们可以包含任意对象,它们可以被索引和切片,它们可以被嵌套——对元组也是如此。但是它们不能被修改:

>>> t = ('foo', 'bar', 'baz', 'qux', 'quux', 'corge')
>>> t[2] = 'Bark!'
Traceback (most recent call last):
  File "<pyshell#65>", line 1, in <module>
    t[2] = 'Bark!'
TypeError: 'tuple' object does not support item assignment

为什么要用元组而不是列表?

  • 当操作一个元组时,程序的执行速度比操作等价列表时要快。(当列表或元组很小时,这可能不会被注意到。)

  • 有时候你不希望数据被修改。如果集合中的值在程序的生命周期中保持不变,使用元组而不是列表可以防止意外修改。

  • 您将很快遇到另一种 Python 数据类型,称为 dictionary它需要一个不可变类型的值作为其组件之一。元组可以用于此目的而列表则不能。

在 Python REPL 会话中,您可以同时显示多个对象的值,方法是在>>>提示符下直接输入这些值,用逗号分隔:

>>> a = 'foo'
>>> b = 42
>>> a, 3.14159, b
('foo', 3.14159, 42)

Python 在括号中显示响应,因为它隐式地将输入解释为元组。

关于元组定义有一个特性您应该知道。当定义一个空元组或者一个有两个或更多元素的元组时没有歧义。Python 知道您正在定义一个元组:

>>> t = ()
>>> type(t)
<class 'tuple'>
>>> t = (1, 2)
>>> type(t)
<class 'tuple'>
>>> t = (1, 2, 3, 4, 5)
>>> type(t)
<class 'tuple'>

但是当你试图用一个条目定义一个元组时会发生什么呢:

>>> t = (2)
>>> type(t)
<class 'int'>

*Doh*由于圆括号也用于定义表达式中的运算符优先级Python 将表达式(2)简单地作为整数2进行计算,并创建一个int对象。要告诉 Python 您确实想要定义一个单例元组,请在右括号前包含一个尾随逗号(,):

>>> t = (2,)
>>> type(t)
<class 'tuple'>
>>> t[0]
2
>>> t[-1]
2

你可能不需要经常定义一个单元组,但是必须有一种方法。

当您显示单元组时Python 包含了逗号,以提醒您这是一个元组:

>>> print(t)
(2,)

Remove ads

元组分配、打包和解包

正如您在上面已经看到的,包含几个项目的文字元组可以分配给单个对象:

>>> t = ('foo', 'bar', 'baz', 'qux')

当这种情况发生时,就好像元组中的项目已经被“打包”到对象中:

tuple packing

Tuple Packing
>>> t
('foo', 'bar', 'baz', 'qux')
>>> t[0]
'foo'
>>> t[-1]
'qux'

如果该“打包”对象随后被分配给新的元组,则各个项被“解包”到元组中的对象中:

tuple unpacking

Tuple Unpacking
>>> (s1, s2, s3, s4) = t
>>> s1
'foo'
>>> s2
'bar'
>>> s3
'baz'
>>> s4
'qux'

解包时,左侧变量的数量必须与元组中值的数量相匹配:

>>> (s1, s2, s3) = t
Traceback (most recent call last):
  File "<pyshell#16>", line 1, in <module>
    (s1, s2, s3) = t
ValueError: too many values to unpack (expected 3)

>>> (s1, s2, s3, s4, s5) = t
Traceback (most recent call last):
  File "<pyshell#17>", line 1, in <module>
    (s1, s2, s3, s4, s5) = t
ValueError: not enough values to unpack (expected 5, got 4)

打包和解包可以合并到一个语句中进行复合赋值:

>>> (s1, s2, s3, s4) = ('foo', 'bar', 'baz', 'qux')
>>> s1
'foo'
>>> s2
'bar'
>>> s3
'baz'
>>> s4
'qux'

同样,赋值左边元组中元素的数量必须等于右边的数量:

>>> (s1, s2, s3, s4, s5) = ('foo', 'bar', 'baz', 'qux')
Traceback (most recent call last):
  File "<pyshell#63>", line 1, in <module>
    (s1, s2, s3, s4, s5) = ('foo', 'bar', 'baz', 'qux')
ValueError: not enough values to unpack (expected 5, got 4)

在像这样的赋值和少数其他情况下Python 允许省略通常用于表示元组的括号:

>>> t = 1, 2, 3
>>> t
(1, 2, 3)

>>> x1, x2, x3 = t
>>> x1, x2, x3
(1, 2, 3)

>>> x1, x2, x3 = 4, 5, 6
>>> x1, x2, x3
(4, 5, 6)

>>> t = 2,
>>> t
(2,)

无论是否包含括号都是一样的,所以如果您对是否需要它们有任何疑问,请继续包含它们。

元组赋值考虑到了一点奇怪的 Python 习惯。在编程时,您经常需要交换两个变量的值。在大多数编程语言中,有必要在交换发生时将其中一个值存储在临时变量中,如下所示:

>>> a = 'foo'
>>> b = 'bar'
>>> a, b
('foo', 'bar')

>>># We need to define a temp variable to accomplish the swap.
>>> temp = a
>>> a = b
>>> b = temp

>>> a, b
('bar', 'foo')

在 Python 中,交换可以通过一个元组赋值来完成:

>>> a = 'foo'
>>> b = 'bar'
>>> a, b
('foo', 'bar')

>>># Magic time!
>>> a, b = b, a

>>> a, b
('bar', 'foo')

任何曾经不得不使用临时变量交换值的人都知道,能够在 Python 中这样做是现代技术成就的顶峰。再也没有比这更好的了。

Remove ads

结论

本教程涵盖了 Python 列表元组的基本属性,以及如何操作它们。您将在 Python 编程中广泛使用这些工具。

列表的主要特征之一是它是有序的。列表中元素的顺序是该列表的固有属性,不会改变,除非列表本身被修改。(元组也是一样,当然除了不能修改。)

下一篇教程将向您介绍 Python **字典:**一种无序的复合数据类型。请继续阅读!

***参加测验:***通过我们的交互式“Python 列表和元组”测验来测试您的知识。完成后,您将收到一个分数,以便您可以跟踪一段时间内的学习进度:

参加测验

*« Strings in PythonLists and Tuples in PythonDictionaries in Python »

立即观看**本教程有真实 Python 团队创建的相关视频课程。和写好的教程一起看,加深理解:Python 中的列表和元组*********