首页 > Python笔记 阅读数:18

Python tuple元组完全攻略

法国启蒙思想家孟德斯鸠(Montesquieu)在其著作《论法的精神》中,提出一句名言:“一切有权力的人都容易滥用权力,这是万古不变的一条经验。”

或许由于 Python 的设计者们觉得列表的“权力”过大,过于“随心所欲”,于是就发明了它的孪生兄弟——元组。元组与列表非常相似,同样可以用索引访问,也同样可以嵌套。不同之处仅在于,元组中的元素一旦创建,便不能修改,它有点像常量版本的列表。故此,也有人将其称为“带上枷锁的列表”。

不同于列表的标识(一对方括号[ ]),元组使用一对圆括号( )将元素囊括其中。创建元组非常简单,只需要在圆括号中添加元素,并用逗号将元素隔开即可。代码如下所示:
In [1]: tup1 = ()  #创建空元组
In [2]: type(tupl) #查询 tup 类型
Out[2]: tupie

如果元组中只包含一个元素,则需要在元素后面添加逗号,代码如下所示:
In [3]: tup1 = (100,)     #创建只包含一个元素的元组
In [4]: type(tup1)        #显示tupl类型
Out[4]: tuple             #元组

在上述代码的 In [3] 处,如果我们忘记了第一个元素后面的“宝贵”逗号,会怎么样呢?请看下面的程序:
In [5]: tupl = (100)    #尝试创建包含单个元素的元组,但忘记了逗号
In [6]: type(tup1)      #显示tup1类型
Out[6]: int             #整型

从上面 Out[6] 处的输出可以看出,编译器把 In [5] 处的 tup1 当作一个整型对象了,100 的外围括号( )仅仅是一个摆设。由此可见,对于元组而言,逗号甚至比圆括号( )更具有身份象征意义。

有时,甚至去掉圆括号( ),而仅仅保留逗号,也能定义一个元组,代码如下所示:
In [7]: tup2 = 'a', 'b', 2,100    #定义一个没有括号的元组
In [8]: type(tup2)                #显示 tup2 类型
Out[8]: tuple

对元组的操作与列表类似,下标索引也是从 0 开始的,也可以进行分片操作等。
In [9]: tup2[:2]   #获取元组中的前两个元素
Out[9]: ('a', 'b')

需要注意的是,元组的分片操作会临时产生一个新的元组,它不会更改原先的元组。

同样,我们可以用加号+操作符连接两个或多个元组,返回一个新的元组,代码如下所示:
In [10]: a = (11,2,3)        #定义元组 a
In [11]: b = (4, 5,6)        #定义元组 b
In [12]: c = a + b           #连接元组 3 和 b,将返回结果赋值给元组c
In [13]: print (a, b, c)     #输出 a、b 和 c 这三个元组
(1, 2, 3) (4, 5, 6) (1, 2, 3, 4, 5, 6)

元组和其他序列对象(如列表、字符串等)是可以相互转换的,代码如下所示:
In [14]: alist = [11, 22, 33]              #定义一个列表
In [15]: atuple = tuple(alist)             #将列表转换为元组,此处 tuple 为关键字
In [16]: atuple                            #验证输出,圆括号表明它已是一个元组
Out[16]: (11, 22, 33)
In [17]: newtuple = tuple('Hello World!')  #将字符串转换为元组
In [18]: newtuple                          #验证输出
Out[18]: ('H', 'e', '1', '1', 'o', ' ' , 'w', 'o',  'r', '1', 'd', '!')

由于元组的“常量”属性,其内部元素的值一旦确定下来,便无法修改,示例代码如下:
In [19]: tup3 =「语文', "chemistry", 97, 2.0) #将一个元组赋值给变量 tupl
In [20]: print(tup3[1])                       #打印元组 tupl 中索引值为 1 的元素
chemistry
In [21]: tup3[1] = 'English'                  #尝试修改元组 tupl 中索引值为 1 的元素值,失败!
TypeError: 'tuple' object does not support item assignment

上面的错误信息明确告诉我们,元组内的元素是不支持修改的。但有时候一定要修改元组,这时该怎么办呢?可以通过迂回的“曲线救国”策略来完成,请参考如下代码:
In [22]: tup4 =「语文',''chemistry",  97, 2.0)        #创建元组
In [23]: id(tup4)                                      #查看原始 tup4 的地址
Out[23]: 143180808
In [24]: tup4 = tup4[:2] + ("zhangsan",) + tup4[2:]    # 连接元组
In [25]: id(tup4)                                      #再次查看原始 tup4 的地址
Out[25]: 139898952
In [26]: tup4                                          #输出元组tup4的值
Out[26]:('语文', 'chemistry', 'zhangsan', 97, 2.0)

从上面的代码中可以看出,至少在表面上,原本牢不可变的元组 tup4 中插入了一个新的元素“zhangsan”。插入工作是这样完成的:在输入行 In [24] 处,先通过元组分片技术,以第 2 个元素为基点,将原始元组拆分为两个部分 tup4[:2] 和 tup4[2:];然后在中间插入一个("zhangsan",)。

对于多个不同元组的连接,通过加法操作符+可以完成。此时,Python 解释器会生成一个新元组(即开辟了新的内存空间),然后将原来的变量名(tup4)指向这个连接好的新元组,旧的同名元组被销毁。

再次强调的是,新插入元素外的那对圆括号不可少,而其内部的逗号更不可少,因为它不仅是与后续元素之间的分隔符,还是一个元组的核心标志。也就是说,在 In [24] 处,实际上完成了三个元组的拼接。

这个手法姑且称为“狸猫换太子”,因为此 tup4 已非彼 tup4。我们可以通过获取对象地址的内置函数 id( ) 来查看前后 tup4 的地址,对比输出行 Out[23] 和 Out[25] 给出的结果,可以发现,二者的地址(可理解为标识对象存在的身份证号码)完全不同,虽然对外宣示的名称都是 tup4,但在 Python 底层,它们早已“物是人非”。

相关文章