Python从入门到精通
上QQ阅读APP看书,第一时间看更新

5.3 元组

元组(tuple)是Python中另一个重要的序列结构,与列表类似,也是由一系列按特定顺序排列的元素组成。但是它是不可变序列。因此,元组也可以称为不可变的列表。在形式上,元组的所有元素都放在一对小括号“()”中,两个相邻元素间使用逗号“,”分隔。在内容上,可以将整数、实数、字符串、列表、元组等任何类型的内容放入元组中,并且同一个元组中,元素的类型可以不同,因为它们之间没有任何关系。通常情况下,元组用于保存程序中不可修改的内容。

说明

从元组和列表的定义上看,这两种结构比较相似,那么它们之间有哪些区别呢?它们之间的主要区别就是一个是不可变序列,一个是可变序列。即元组中的元素不可以单独修改,而列表则可以任意修改。

5.3.1 元组的创建和删除

在Python中提供了多种创建元组的方法,下面分别进行介绍。

1.使用赋值运算符直接创建元组

同其他类型的Python变量一样,创建元组时,也可以使用赋值运算符“=”直接将一个元组赋值给变量。具体的语法格式如下:

    tuplename = (element 1,element 2,element 3,...,element n)

其中,tuplename表示元组的名称,可以是任何符合Python命名规则的标识符;element 1、element 2、element 3、element n表示元组中的元素,个数没有限制,并且只要是Python支持的数据类型就可以。

注意

创建元组的语法与创建列表的语法类似,只是创建列表时使用的是中括号“[]”,而创建元组时使用的是小括号“()”。

例如,下面定义的都是合法的元组。

01  num = (7,14,21,28,35,42,49,56,63)
02  ukguzheng = ("渔舟唱晚","高山流水","出水莲","汉宫秋月")
03  untitle = ('Python',28,("人生苦短","我用Python"),["爬虫","自动化运维","云计算","Web开发"])
04  python = ('优雅',"明确",'''简单''')

在Python中,虽然元组是使用一对小括号将所有的元素括起来。但是实际上,小括号并不是必需的,只要将一组值用逗号分隔开来,Python就可以认为它是元组。例如,下面的代码定义的也是元组。

    ukguzheng = "渔舟唱晚","高山流水","出水莲","汉宫秋月"

在IDLE中输出该元组后,将显示以下内容。

    ('渔舟唱晚', '高山流水', '出水莲', '汉宫秋月')

如果要创建的元组只包括一个元素,则需要在定义元组时,在元素的后面加一个逗号“,”。例如,下面的代码定义的就是包括一个元素的元组。

    verse = ("一片冰心在玉壶",)

在IDLE中输出verse,将显示以下内容。

    ('一片冰心在玉壶',)

而下面的代码,则表示定义一个字符串。

    verse = ("一片冰心在玉壶")

在IDLE中输出verse,将显示以下内容。

    一片冰心在玉壶

说明

在Python中,可以使用type()函数测试变量的类型。例如下面的代码:

01  verse1 = ("一片冰心在玉壶",)
02  print("verse1的类型为",type(verse1))
03  verse2 = ("一片冰心在玉壶")
04  print("verse2的类型为",type(verse2))

在IDLE中执行上面的代码,将显示以下内容。

    verse1的类型为 <class 'tuple'>
    verse2的类型为 <class 'str'>
2.创建空元组

在Python中,也可以创建空元组,例如,要创建一个名称为emptytuple的空元组,可以使用下面的代码:

    emptytuple = ()

空元组可以应用在为函数传递一个空值或者返回空值时。例如,定义一个函数必须传递一个元组类型的值,而我们还不想为它传递一组数据,那么就可以创建一个空元组传递给它。

3.创建数值元组

在Python中,可以使用tuple()函数直接将range()函数循环出来的结果转换为数值元组。

说明

关于range()函数的详细介绍请参见4.3.2节。

tuple()函数的基本语法如下:

    tuple(data)

其中,data表示可以转换为元组的数据,其类型可以是range对象、字符串、元组或者其他可迭代类型的数据。

例如,创建一个10~20(不包括20)中所有偶数的元组,可以使用下面的代码。

    tuple(range(10, 20, 2))

运行上面的代码后,将得到下面的列表。

    (10, 12, 14, 16, 18)

说明

使用tuple()函数不仅能通过range对象创建元组,还可以通过其他对象创建元组。

4.删除元组

对于已经创建的元组,不再使用时,可以使用del语句将其删除。语法格式如下:

    del tuplename

其中,tuplename为要删除元组的名称。

说明

del语句在实际开发时,并不常用。因为Python自带的垃圾回收机制会自动销毁不用的元组,所以即使我们不手动将其删除,Python也会自动将其回收。

例如,定义一个名称为verse的元组,然后再应用del语句将其删除,可以使用下面的代码。

01  verse = ("自古逢秋悲寂寥","我言秋日胜春朝","晴空一鹤排云上","便引诗情到碧霄")
02  del verse

场景模拟:假设有一家伊米咖啡馆,只提供6种咖啡,并且不会改变。请使用元组保存该咖啡馆里提供的咖啡名称。

【例5.5】 使用元组保存咖啡馆里提供的咖啡名称。(实例位置:资源包\TM\sl\05\05)

在IDLE中创建一个名称为cafe_coffeename.py的文件,然后在该文件中定义一个包含6个元素的元组,内容为伊米咖啡馆里的咖啡名称,并且输出该元组,代码如下:

运行结果如图5.17所示。

图5.17 使用元组保存咖啡馆里提供的咖啡名称

5.3.2 访问元组元素

在Python中,如果想将元组的内容输出也比较简单,可以直接使用print()函数。例如,要想打印上面元组中的untitle元组,则可以使用下面的代码。

    print(untitle)

执行结果如下:

    ('Python', 28, ('人生苦短', '我用Python'), ['爬虫', '自动化运维', '云计算', 'Web开发'])

从上面的执行结果中可以看出,在输出元组时,是包括左右两侧的小括号的。如果不想输出全部元素,也可以通过元组的索引获取指定的元素。例如,要获取元组untitle中索引为0的元素,可以使用下面的代码。

    print(untitle[0])

执行结果如下:

    Python

从上面的执行结果中可以看出,在输出单个元组元素时,不包括小括号,如果是字符串,还不包括左右的引号。

另外,对于元组也可以采用切片方式获取指定的元素。例如,要访问元组untitle中前3个元素,可以使用下面的代码。

    print(untitle[:3])

执行结果如下:

    ('Python', 28, ('人生苦短', '我用Python'))

同列表一样,元组也可以使用for循环进行遍历。下面通过一个具体的实例演示如何通过for循环遍历元组。

场景模拟:仍然是伊米咖啡馆,这时有客人到了,服务员向客人介绍本店提供的咖啡。

【例5.6】 使用for循环列出咖啡馆里的咖啡名称。(实例位置:资源包\TM\sl\05\06)

在IDLE中创建一个名称为cafe_coffeename.py的文件,然后在该文件中定义一个包含6个元素的元组,内容为伊米咖啡馆里的咖啡名称,然后应用for循环语句输出每个元组元素的值,即咖啡名称,并且在后面加上“咖啡”二字,代码如下:

运行结果如图5.18所示。

图5.18 使用元组保存咖啡馆里提供的咖啡名称

另外,元组还可以使用for循环和enumerate()函数结合进行遍历。下面通过一个具体的实例演示如何通过for循环和enumerate()函数结合遍历元组。

说明

enumerate()函数用于将一个可遍历的数据对象(如列表或元组)组合为一个索引序列,同时列出数据和数据下标,一般在for循环中使用。

【例5.7】 使用元组实现每两行一句输出古诗《长歌行》。(实例位置:资源包\TM\sl\05\07)

本实例将在实例5.2的基础上进行修改,将列表修改为元组,其他内容不变,修改后的代码如下:

说明

在上面的代码中,在print()函数中使用“, end=''”表示不换行输出,即下一条print()函数的输出内容会和这个内容在同一行输出。

运行结果如图5.19所示。

图5.19 每两行一句输出古诗《长歌行》

5.3.3 修改元组

场景模拟:仍然是伊米咖啡馆,因为巴西咖啡断货,所以店长想要把它换成土耳其咖啡。

【例5.8】 替换巴西咖啡为土耳其咖啡。(实例位置:资源包\TM\sl\05\08)

在IDLE中创建一个名称为cafe_replace.py的文件,然后在该文件中定义一个包含6个元素的元组,内容为伊米咖啡馆里的咖啡名称,然后修改其中的第5个元素的内容为“土耳其”,代码如下:

运行结果如图5.20所示。

图5.20 替换巴西咖啡为土耳其咖啡出现异常

元组是不可变序列,所以我们不能对它的单个元素值进行修改,但是元组也不是完全不能修改。我们可以对元组进行重新赋值。例如,下面的代码是允许的。

执行结果如下。

    新元组 ('蓝山', '卡布奇诺', '曼特宁', '摩卡', '土耳其', '哥伦比亚')

从上面的执行结果可以看出,元组coffeename的值已经改变。

另外,还可以对元组进行连接组合。例如,可以使用下面的代码实现在已经存在的元组结尾处添加一个新元组。

01  ukguzheng = ('蓝山','卡布奇诺','曼特宁','摩卡')
02  print("原元组:",ukguzheng)
03  ukguzheng = ukguzheng + ('巴西','哥伦比亚')
04  print("组合后:",ukguzheng)

执行结果如下。

    原元组: ('蓝山', '卡布奇诺', '曼特宁', '摩卡')
    组合后: ('蓝山', '卡布奇诺', '曼特宁', '摩卡', '土耳其', '哥伦比亚')

注意

在进行元组连接时,连接的内容必须都是元组。不能将元组和字符串或者列表进行连接。例如,下面的代码就是错误的。

01  ukguzheng = '蓝山','卡布奇诺','曼特宁','摩卡')
02  ukguzheng = ukguzheng + ['巴西','哥伦比亚']

说明

在进行元组连接时,如果要连接的元组只有一个元素,一定不要忘记后面的逗号。例如,使用下面的代码将生产如图5.21所示的错误。

01  ukguzheng = ('蓝山','卡布奇诺','曼特宁','摩卡')
02  ukguzheng = ukguzheng + ('巴西')

图5.21 在进行元组连接时产生的异常

5.3.4 元组推导式

使用元组推导式可以快速生成一个元组,它的表现形式和列表推导式类似,只是将列表推导式中的中括号“[]”修改为小括号“()”。例如,我们可以使用下面的代码生成一个包含10个随机数的元组。

01  import random  #导入random标准库
02  randomnumber = (random.randint(10,100) for i in range(10))
03  print("生成的元组为:",randomnumber)

执行结果如下:

    生成的元组为: <generator object <genexpr> at 0x0000000003056620>

从上面的执行结果中可以看出,使用元组推导式生成的结果并不是一个元组或者列表,而是一个生成器对象,这一点和列表推导式是不同的。要使用该生成器对象,可以将其转换为元组或者列表。其中,转换为元组使用tuple()函数,而转换为列表则使用list()函数。

例如,使用元组推导式生成一个包含10个随机数的生成器对象,然后将其转换为元组并输出,可以使用下面的代码。

执行结果如下:

    转换后: (76, 54, 74, 63, 61, 71, 53, 75, 61, 55)

要使用通过元组推导器生成的生成器对象,还可以直接通过for循环遍历或者直接使用__next()__方法进行遍历。

说明

在Python 2.x中,__next__()方法为next(),也是用于遍历生成器对象的。

例如,通过生成器推导式生成一个包含3个元素的生成器对象number,然后调用3次__next__()方法输出每个元素,再将生成器对象number转换为元组输出,代码如下:

上面的代码运行后,将显示以下结果。

    0
    1
    2
    转换后: ()

再如,通过生成器推导式生成一个包括4个元素的生成器对象number,然后应用for循环遍历该生成器对象,并输出每个元素的值,最后再将其转换为元组输出,代码如下:

执行结果如下:

    0 1 2 3 ()

从上面的两个示例中可以看出,无论通过哪种方法遍历,如果还想再使用该生成器对象,都必须重新创建一个生成器对象。因为遍历后,原生成器对象已经不存在了。

5.3.5 元组与列表的区别

元组和列表都属于序列,而且它们又都可以按照特定顺序存放一组元素,类型又不受限制,只要是Python支持的类型都可以。那么它们之间有什么区别呢?

简单理解:列表类似于我们用铅笔在纸上写下自己喜欢的歌曲,写错了还可以擦掉。而元组则类似于用钢笔写下的歌曲名字,写上了就擦不掉了,除非换一张纸重写。

列表和元组的区别主要体现在以下5个方面。

(1)列表属于可变序列,它的元素可以随时修改或者删除,而元组属于不可变序列,其中的元素不可以修改,除非整体替换。

(2)列表可以使用append()、extend()、insert()、remove()和pop()等方法实现添加和修改列表元素,而元组则没有这几个方法,因为不能向元组中添加和修改元素。同样,也不能删除元素。

(3)列表可以使用切片访问和修改列表中的元素。元组也支持切片,但是它只支持通过切片访问元组中的元素,不支持修改。

(4)元组比列表的访问和处理速度快。所以如果只需要对其中的元素进行访问,而不进行任何修改,建议使用元组而不使用列表。

(5)列表不能作为字典的键,而元组可以。