上⼀节课为⼤家讲解了
Python
中的列表,它是⼀种容器型数据类型,我们可以通过定义列表类型的变量来保存和操作多个元素。当然,Python
中容器型的数据类型肯定不⽌列表⼀种,接下来我们为⼤家讲解另⼀种重要的容器型数据类型,它的名字叫元组(tuple
)。
定义和使⽤元组
在
Python
中,元组也是多个元素按照⼀定的顺序构成的序列。元组和列表的不同之处在于,元组是不可变类型,这就意味着元组类型的变量⼀旦定义,其中的元素不能再添加或删除,⽽且元素的值也不能进⾏修改。定义元组通常使⽤ ()
字⾯量语法,也建议⼤家使⽤这种⽅式来创建元组。元组类型⽀持的运算符跟列表是⼀样。下⾯的代码演示了元组的定义和运算。
# 定义⼀个三元组
t1 = (30, 10, 55)
# 定义⼀个四元组
t2 = ('骆昊', 40, True, '四川成都')
# 查看变量的类型
print(type(t1), type(t2)) # <class 'tuple'> <class 'tuple'>
# 查看元组中元素的数量
print(len(t1), len(t2)) # 3 4
# 通过索引运算获取元组中的元素
print(t1[0], t1[-3]) # 30 30
print(t2[3], t2[-1]) # 四川成都 四川成都
# 循环遍历元组中的元素
for member in t2:
print(member)
# 成员运算
print(100 in t1) # False
print(40 in t2) # True
# 拼接
t3 = t1 + t2
print(t3) # (30, 10, 55, '骆昊', 40, True, '四川成都')
# 切⽚
print(t3[::3]) # (30, '骆昊', '四川成都')
# ⽐较运算
print(t1 == t3) # False
print(t1 >= t3) # False
print(t1 < (30, 11, 55)) # True
⼀个元组中如果有两个元素,我们就称之为⼆元组;⼀个元组中如果五个元素,我们就称之为五元组。需要提醒⼤家注意的是, ()
表示空元组,但是如果元组中只有⼀个元素,需要加上⼀个逗号,否则
() 就不是代表元组的字⾯量语法,⽽是改变运算优先级的圆括号,所以 ('hello', )
和
(100, )
才是⼀元组,⽽ ('hello')
和
(100)
只是字符串和整数。我们可以通过下⾯的代码来加以验证。
# 空元组
a = ()
print(type(a)) # <class 'tuple'>
# 不是元组
b = ('hello')
print(type(b)) # <class 'str'>
c = (100)
print(type(c)) # <class 'int'>
# ⼀元组
d = ('hello', )
print(type(d)) # <class 'tuple'>
e = (100, )
print(type(e)) # <class 'tuple'>
元组的应⽤场景
讲到这⾥,相信⼤家⼀定迫切的想知道元组有哪些应⽤场景,我们给⼤家举⼏个例⼦。
例⼦
1
:打包和解包操作。
当我们把多个⽤逗号分隔的值赋给⼀个变量时,多个值会打包成⼀个元组类型;当我们把⼀个元组赋值给多个变量时,元组会解包成多个值然后分别赋给对应的变量,如下⾯的代码所示。
# 打包
a = 1, 10, 100
print(type(a), a) # <class 'tuple'> (1, 10, 100)
# 解包
i, j, k = a
print(i, j, k) # 1 10 100
在解包时,如果解包出来的元素个数和变量个数不对应,会引发
ValueError
异常,错误信息为:
too many values to unpack (解包的值太多)或
not enough values to unpack
(解包的值不⾜)。
a = 1, 10, 100, 1000
# i, j, k = a # ValueError: too many values to unpack (expected 3)
# i, j, k, l, m, n = a # ValueError: not enough values to unpack (expected 6,
got 4)
有⼀种解决变量个数少于元素的个数⽅法,就是使⽤星号表达式,我们之前讲函数的可变参数时使⽤过星号表达式。有了星号表达式,我们就可以让⼀个变量接收多个值,代码如下所示。需要注意的是,⽤星号表达式修饰的变量会变成⼀个列表,列表中有0
个或多个元素。还有在解包语法中,星号表达式只能出现⼀次。
a = 1, 10, 100, 1000
i, j, *k = a
print(i, j, k) # 1 10 [100, 1000]
i, *j, k = a
print(i, j, k) # 1 [10, 100] 1000
*i, j, k = a
print(i, j, k) # [1, 10] 100 1000
*i, j = a
print(i, j) # [1, 10, 100] 1000
i, *j = a
print(i, j) # 1 [10, 100, 1000]
i, j, k, *l = a
print(i, j, k, l) # 1 10 100 [1000]
i, j, k, l, *m = a
print(i, j, k, l, m) # 1 10 100 1000 []
需要说明⼀点,解包语法对所有的序列都成⽴,这就意味着对字符串、列表以及我们之前讲到的
range函数返回的范围序列都可以使⽤解包语法。⼤家可以尝试运⾏下⾯的代码,看看会出现怎样的结果。
a, b, *c = range(1, 10)
print(a, b, c)
a, b, c = [1, 10, 100]
print(a, b, c)
a, *b, c = 'hello'
print(a, b, c)
现在我们可以反过来思考⼀下函数的可变参数,可变参数其实就是将多个参数打包成了⼀个元组,可以通过下⾯的代码来证明这⼀点。
def add(*args):
print(type(args), args)
total = 0
for val in args:
total += val
return total
add(1, 10, 20) # <class 'tuple'> (1, 10, 20)
add(1, 2, 3, 4, 5) # <class 'tuple'> (1, 2, 3, 4, 5)
例⼦
2
:交换两个变量的值。
交换两个变量的值是编程语⾔中的⼀个经典案例,在很多编程语⾔中,交换两个变量的值都需要借助⼀个中间变量才能做到,如果不⽤中间变量就需要使⽤⽐较晦涩的位运算来实现。在Python
中,交换两个变量 a
和
b
的值只需要使⽤如下所示的代码。
a, b = b, a
同理,如果要将三个变量
a
、
b
、
c
的值互换,即
b
赋给
a
,
c
赋给
b
,
a
赋给
c
,也可以如法炮制。
a, b, c = b, c, a
需要说明的是,上⾯并没有⽤到打包和解包语法,
Python
的字节码指令中有
ROT_TWO 和
ROT_THREE
这样的指令可以实现这个操作,效率是⾮常⾼的。但是如果有多于三个变量的值要依次互换,这个时候没有直接可⽤的字节码指令,执⾏的原理就是我们上⾯讲解的打包和解包操作。
例⼦
3
:让函数返回多个值。
有的时候⼀个函数执⾏完成后可能需要返回多个值,这个时候元组类型应该是⽐较⽅便的选择。例如,编写⼀个找出列表中最⼤值和最⼩的函数。
def find_max_and_min(items):
"""找出列表中最⼤和最⼩的元素
:param items: 列表
:return: 最⼤和最⼩元素构成的⼆元组
"""
max_one, min_one = items[0], items[0]
for item in items:
if item > max_one:
max_one = item
elif item < min_one:
min_one = item
return max_one, min_one
上⾯函数的
return
语句中有两个值,这两个值会组装成⼀个⼆元组然后返回。所以调用find_max_and_min
函数会得到这个⼆元组,如果愿意也可以通过解包语法将⼆元组中的两个值分别赋给两个变量。