python基础数据类型元组(tuple)详解

1.python元组的定义

python的元组是有序且不可被修改的数据集合,使用小括号() 进行定义,元组内的元素之间使用逗号分隔。从形式上观察,除了用小括号()代替了中括号[],元组几乎与列表一样,但元组有自己独特的特性:元组一旦被创建就无法被修改,新增,删除,修改这些操作都无法在元组上进行。

2. 创建元组

使用小括号,可以创建一个空的元组

t = ()
print(type(t))      # <class 'tuple'>

创建只有一个元素的元组

t = (5, )
print(type(t))    # <class 'tuple'>

即便元组里只有一个元素,也要写入一个逗号,因为小括号()既可以表示为元组的定义形式,也可以作为括号表示算数运算时的优先级,如果没有逗号,(5) 将被解析成int类型的5,在下面的表达式里,()不在被认为是元组的定义形式。

>>> 5 == (5)
True

创建拥有多个元素的元组

t = ('python', 'java', 'php')
print(t)            # ('python', 'java', 'php')

使用内置函数tuple创建元组

t1 = tuple(['python', 'java', 'php'])
print(t1)    # ('python', 'java', 'php')

t2 = tuple('python')
print(t2)    # ('p', 'y', 't', 'h', 'o', 'n')

3. 获取元组里的值

获取元组里的值,方法与从列表里获取值一样,必须提供索引,通过[]索引访问的方式获得索引对应的值。

t = tuple(['python', 'java', 'php'])
print(t[0])   # python

通过for循环同样可以对元组进行遍历

t = tuple(['python', 'java', 'php'])

for item in t:
    print(item)

4. 元组的切片操作

元组同样支持切片操作,这方面与列表完全一致

t = tuple(['python', 'java', 'php'])

print(t[1:])   # ('java', 'php')

只要不修改元组,对列表的一切操作都可以移植到元组身上。

5. 元组真的不可以被修改么

元组是不可变对象,但有人却提出的反例

t = ([1, 2, 3], ['python'])
t[0][0] = 3
print(t)

元组t里有两个元组,两个元组都是列表,程序执行的结果是

([3, 2, 3], ['python'])

看起来,元组似乎成功被修改了,然而这并不是事实,元组没有被修改,被修改的是列表里的元素,准确的说是t[0]被修改了,而t没有被修改。

当我们强调元组是不可变对象时,是指无法通过索引修改元组里的元素,类似下面的代码是无法被执行的

t = ([1, 2, 3], ['python'])
t[0] = '修改'

程序会报错

TypeError: 'tuple' object does not support item assignment

t[0] 是列表,我们不能将元组的第一个元素修改为其他对象,但t[0]作为列表本身是可以被修改的,但修改以后,t[0]还是原来的那个列表,只是列表里的内容发生了变化,元组t没有发生变化,仍然只有两个元素,通过内置函数id输出t[0]的内存地址能让你更清楚的认识到元组没有被修改。

t = ([1, 2, 3], ['python'])
print(id(t[0]))     # 2161425011080
t[0][0] = 3
print(id(t[0]))     # 2161425011080

修改前后,t[0]的内存地址不变,从元组的视角来看,它内部存储的第一个元素没有发生改变。

6. 元组与列表的区别

很多人认为元组就是一个不可变的列表,虽然很形象,但并不准确。python在元组内部做了很多优化,既节省了内存又提高了性能,元组有自己特殊的使用场景,这是列表无法替代的。

6.1 函数返回多个结果时,元组可以作为返回值

def func(x, y):
    return x, y, x+y

res = func(2, 3)
print(res)

当函数有多个返回值时,最终以元组的形式返回,程序输出结果为

(2, 3, 5)

当函数返回多个结果时,以列表的形式返回,难道不也是可行的么?从程序设计的角度看,函数返回多个结果时,以元组形式返回好于以列表形式返回,原因在于列表是可变对象,这意味着函数的返回结果是可修改的,那么函数在使用时就容易出现修改函数返回值的情况。

某些情况下,我们不希望函数的返回值被他人修改,元组恰好满足了我们的要求,如果函数本意就是返回一个列表,那么在renturn时,就应该直接返回列表,而不是返回多个结果。

6.2 元组作为函数的可变参数

def func(*args):
    print(args, type(args))

func(3, 4, 5)

定义一个支持可变参数的函数时,args的类型是元组,在函数内可以从args中依次取出传入的参数,那么同样的问题,为什么不是列表呢?还是从程序设计的角度出发,如果args被设计成列表,由于列表可以被修改,保不齐某个程序员在实现函数的时候修改了args,传入的参数被修改了,或是增加,或是减少,这样就会引发不可预知的错误。

但现在,python将其设计成元组,元组无法修改,你在函数内部无法修改传入的参数,这就保证了函数入参的安全性。

6.3 元组可以作为字典的key,可以存储到集合中

想要成为字典的key,或是存储到集合中,必须满足可hash这个条件,所有的可变对象,诸如列表,集合,字典都不能做key,但元组可以,在一些特定情境下,用元组做key,非常实用,比如下面这个练习题目

题目要求:已知有两个列表

lst1 = [3, 5, 6, 1, 2, 4, 7]
lst2 = [6, 5, 4, 7, 3, 8]

从两个列表里各取出一个数,他们的和如果为10,则记录下来,请写程序计算,这种组合一共有几种,分别是什么,要求组合不能重复。

从lst1中取3, lst2中取7,这对组合满足要求,从lst1中取7,lst2中取3,也满足要求,但是这个组合已经存在了,因此不算。

使用嵌套循环,就可以轻易的完成这个题目,但是这中间过程要去除掉重复的组合,正好可以利用元组,算法实现如下

lst1 = [3, 5, 6, 1, 2, 4, 7]
lst2 = [6, 5, 4, 7, 3, 8]

res_set = set()
for i in lst1:
    for j in lst2:
        if i + j == 10:
            if i > j:
                res_set.add((j, i))
            else:
                res_set.add((i, j))

print(res_set)

程序输出结果

{(3, 7), (4, 6), (5, 5), (2, 8)}

去重的过程恰好利用了元组,将组合以元组的形式存储到集合中,利用集合的不重复性来达到去重的目的,元组里第一个元素是组合中较小的那个数

扫描关注, 与我技术互动

QQ交流群: 211426309

加入知识星球, 每天收获更多精彩内容

分享日常研究的python技术和遇到的问题及解决方案