元组
Python中的元组容器序列(tuple)与列表容器序列(list)具有极大的相似之处,因此也常被称为不可变的列表。
但是两者之间也有很多的差距,元组侧重于数据的展示,而列表侧重于数据的存储与操作。
它们非常相似,虽然都可以存储任意类型的数据,但是一个元组定义好之后就不能够再进行修改。
元组特性
元组特性如下:
- 元组属于线性容器序列
- 元组属于不可变类型,即对象本身的属性不会根据外部变化而变化
- 元组底层由顺序存储组成,而顺序存储是线性结构的一种
基本声明
以下是使用类实例化的形式进行对象声明:
tpl = tuple((1, 2, 3, 4, 5))
print("value : %r\ntype : %r" % (tpl, type(tpl)))
# value : (1, 2, 3, 4, 5)
# type : <class 'tuple'>
也可以选择使用更方便的字面量形式进行对象声明,使用逗号对数据项之间进行分割:
tpl = 1, 2, 3, 4, 5
print("value : %r\ntype : %r" % (tpl, type(tpl)))
# value : (1, 2, 3, 4, 5)
# type : <class 'tuple'>
为了美观,我们一般会在两侧加上(),但是要确定一点,元组定义是用逗号来分隔数据项,而并非是用()包裹数据项:
tpl = (1, 2, 3, 4, 5)
print("value : %r\ntype : %r" % (tpl, type(tpl)))
# value : (1, 2, 3, 4, 5)
# type : <class 'tuple'>
多维元组
当一个元组中嵌套另一个元组,该元组就可以称为多维元组。
如下,定义一个2维元组:
tpl = (1, 2, ("三", "四"))
print("value : %r\ntype : %r" % (tpl , type(tpl)))
# value : (1, 2, ('三', '四'))
# type : <class 'tuple'>
续行操作
在Python中,元组中的数据项如果过多,可能会导致整个元组太长,太长的元组是不符合PEP8规范的。
- 每行最大的字符数不可超过79,文档字符或者注释每行不可超过72
Python虽然提供了续行符\,但是在元组中可以忽略续行符,如下所示:
tpl = (
1,
2,
3,
4,
5
)
print("value : %r\ntype : %r" % (tpl, type(tpl)))
# value : (1, 2, 3, 4, 5)
# type : <class 'tuple'>
类型转换
元组支持与布尔型、字符串、列表、以及集合类型进行类型转换:
tpl = (1, 2, 3)
bTpl = bool(tpl)
strTpl = str(tpl)
lstTpl = list(tpl)
setTpl = set(tpl)
print("value : %r\ntype : %r" % (bTpl, type(bTpl)))
print("value : %r\ntype : %r" % (strTpl, type(strTpl)))
print("value : %r\ntype : %r" % (lstTpl, type(lstTpl)))
print("value : %r\ntype : %r" % (setTpl, type(setTpl)))
# value : True
# type : <class 'bool'>
# value : '(1, 2, 3)'
# type : <class 'str'>
# value : [1, 2, 3]
# type : <class 'list'>
# value : {1, 2, 3}
# type : <class 'set'>
如果一个2维元组遵循一定的规律,那么也可以将其转换为字典类型:
tpl = (("k1", "v1"), ("k2", "v2"), ("k3", "v3"))
dictTuple = dict(tpl)
print("value : %r\ntype : %r" % (dictTuple, type(dictTuple)))
# value : {'k1': 'v1', 'k2': 'v2', 'k3': 'v3'}
# type : <class 'dict'>
索引操作
元组由于是线性结构,故支持索引和切片操作
但只针对获取,不能对其内部数据项进行修改。
使用方法参照列表的索引切片一节。
绝对引用
元组拥有绝对引用的特性,无论是深拷贝还是浅拷贝,都不会获得其副本,而是直接对源对象进行引用。
但是列表没有绝对引用的特性,代码验证如下:
>>> import copy
>>> # 列表的深浅拷贝均创建新列表...
>>> oldLi = [1, 2, 3]
>>> id(oldLi)
4542649096
>>> li1 = copy.copy(oldLi)
>>> id(li1)
4542648840
>>> li2 = copy.deepcopy(oldLi)
>>> id(li2)
4542651208
>>> # 元组的深浅拷贝始终引用老元组
>>> oldTup = (1, 2, 3)
>>> id(oldTup)
4542652920
>>> tup1 = copy.copy(oldTup)
>>> id(tup1)
4542652920
>>> tup2 = copy.deepcopy(oldTup)
>>> id(tup2)
4542652920
Python为何要这样设计?其实仔细想想不难发现,元组不能对其进行操作,仅能获取数据项。
那么也就没有生成多个副本提供给开发人员操作的必要了,因为你修改不了元组,索性直接使用绝对引用策略。
值得注意的一点:[:]也是浅拷贝,故对元组来说属于绝对引用范畴。
元组的陷阱
Leonardo Rochael在2013年的Python巴西会议提出了一个非常具有思考意义的问题。
我们先来看一下:
>>> t = (1, 2, [30, 40])
>>> t[-1] += [50, 60]
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: 'tuple' o