Python从入门到精通day10

常用数据结构之元组

前两节课我们详细学习了列表(list)------一种灵活的可变容器,能轻松实现元素的添加、删除和修改。但在实际开发中,我们有时需要"不可修改"的容器来存储固定数据(如配置信息、坐标值等),这时就需要用到 Python 中的另一种核心容器类型------元组(tuple)。本节课我们将从定义、运算、核心特性到实际应用,全面掌握元组的用法。

元组的定义和运算

元组和列表类似,都是按顺序存储多个元素的有序序列 ,支持索引、切片等操作。但二者最核心的区别是:元组是不可变类型 ------一旦定义,元素的个数、值和顺序都无法修改(包括添加、删除、修改元素)。试图修改元组元素会直接触发TypeError错误,这是元组最关键的特性。

1. 元组的定义语法

定义元组主要有两种方式,最常用的是字面量语法 (用圆括号 () 包裹元素,元素间用逗号分隔):

  • 空元组:t = ()

  • 多元组:t = (元素1, 元素2, 元素3, ...)

2. 元组支持的运算

虽然元组不可变,但它支持列表的绝大多数运算,包括:索引、切片、长度计算、成员判断、拼接、比较等。运算语法和列表一样,无需额外记忆。

复制代码
# 定义一个三元组
t1 = (35, 12, 98)
# 定义一个四元组
t2 = ('南阳', 45, True, '四川成都')

# 查看变量的类型
print(type(t1))  # <class 'tuple'>
print(type(t2))  # <class 'tuple'>

# 查看元组中元素的数量
print(len(t1))  # 3
print(len(t2))  # 4

# 索引运算
print(t1[0])    # 35
print(t1[2])    # 98
print(t2[-1])   # 四川成都

# 切片运算
print(t2[:2])   # ('南阳', 45)
print(t2[::3])  # ('南阳', '四川成都')

# 循环遍历元组中的元素
for elem in t1:
    print(elem)

# 成员运算
print(12in t1)         # True
print(99in t1)         # False
print('Hao'notin t2)  # True

# 拼接运算
t3 = t1 + t2
print(t3)  # (35, 12, 98, '南阳', 43, True, '四川成都')

# 比较运算
print(t1 == t3)            # False
print(t1 >= t3)            # False
print(t1 <= (35, 11, 99))  # False

注意:一元组(重点避坑):t = (元素, )------必须加逗号!否则 (元素) 会被当作普通括号(改变运算优先级),而非元组。

所以('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'>

打包和解包操作

当我们把多个用逗号分隔的值赋给一个变量时,多个值会打包成一个元组类型;当我们把一个元组赋值给多个变量时,元组会解包成多个值然后分别赋给对应的变量,如下面的代码所示。

复制代码
# 打包操作
a = 1, 10, 100
print(type(a))  # <class 'tuple'>
print(a)        # (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)

交换变量的值

交换变量的值是写代码时经常用到的一个操作,但是在很多编程语言中,交换两个变量的值都需要借助一个中间变量才能做到,如果不用中间变量就需要使用比较晦涩的位运算来实现。在 Python 中,交换两个变量ab的值只需要使用如下所示的代码。

复制代码
a, b = b, a

同理,如果要将三个变量abc的值互换,即b的值赋给ac的值赋给ba的值赋给c,也可以如法炮制。

复制代码
a, b, c = b, c, a

需要说明的是,上面的操作并没有用到打包和解包语法,Python 的字节码指令中有ROT_TWOROT_THREE这样的指令可以直接实现这个操作,效率是非常高的。但是如果有多于三个变量的值要依次互换,这个时候是没有直接可用的字节码指令的,需要通过打包解包的方式来完成变量之间值的交换。

元组和列表的比较

这里还有一个非常值得探讨的问题,Python 中已经有了列表类型,为什么还需要元组这样的类型呢?这个问题对于初学者来说似乎有点困难,不过没有关系,我们先抛出观点,大家可以一边学习一边慢慢体会。

    1. 元组是不可变类型,不可变类型更适合多线程环境,因为它降低了并发访问变量的同步化开销。关于这一点,我们会在后面讲解并发编程的时候跟大家一起探讨。
    1. 元组是不可变类型,通常不可变类型在创建时间上优于对应的可变类型 。我们可以使用timeit模块的timeit函数来看看创建保存相同元素的元组和列表各自花费的时间,timeit函数的number参数表示代码执行的次数。下面的代码中,我们分别创建了保存19的整数的列表和元组,每个操作执行10000000次,统计运行时间。

      import timeit

      print('%.3f 秒' % timeit.timeit('[1, 2, 3, 4, 5, 6, 7, 8, 9]', number=10000000))
      print('%.3f 秒' % timeit.timeit('(1, 2, 3, 4, 5, 6, 7, 8, 9)', number=10000000))

    输出:

    复制代码
    0.635 秒
    0.078 秒

    说明 :上面代码的执行结果因软硬件系统而异,在我目前使用的电脑上,执行10000000次创建列表的操作时间是0.635秒,而执行10000000次创建元组的操作时间是0.078秒,显然创建元组更快且二者时间上有数量级的差别。大家可以在自己的电脑上执行这段代码,把你的执行结果放到评论区,看看谁的电脑更厉害。

当然,Python 中的元组和列表类型是可以相互转换的,我们可以通过下面的代码来完成该操作。

复制代码
infos = ('南阳', 43, True, '四川成都')
# 将元组转换成列表
print(list(infos))  # ['南阳', 43, True, '四川成都']

frts = ['apple', 'banana', 'orange']
# 将列表转换成元组
print(tuple(frts))  # ('apple', 'banana', 'orange')

今日总结

  1. 核心定义:元组是有序、不可变的容器,用 () 定义,一元组必须加逗号;

  2. 核心特性:不可变(增删改元素会报错),支持列表的绝大多数运算(索引、切片、拼接等);

  3. 核心操作:打包(自动将多值打包成元组)和解包(将元组拆分给多变量),星号表达式可处理变量与元素个数不匹配的情况;

  4. 经典应用:交换变量、函数多返回值、存储固定数据;

  5. 选型原则:数据不变用元组(性能优、防误改),数据可变用列表(灵活)。

元组和列表是 Python 中最基础的两个容器类型,二者的核心区别(可变性)决定了它们的适用场景。掌握好它们的用法和选型技巧,能让你的代码更高效、更安全。

相关推荐
xdpcxq10292 小时前
Apache 详解 在 Ubuntu 24 中安装和配置 Apache
linux·ubuntu·apache
mftang2 小时前
Python 获取当前目录的多种方法
python
晨非辰2 小时前
C++波澜壮阔40年|类和对象篇:拷贝构造与赋值重载的演进与实现
运维·开发语言·c++·人工智能·后端·python·深度学习
未来龙皇小蓝2 小时前
策略模式:Spring Bean策略与枚举 Lambda策略
java·windows·spring boot·spring·策略模式
多米Domi0112 小时前
0x3f 第36天 外卖8,9,树
数据结构·python·算法·leetcode
天才测试猿2 小时前
Chrome浏览器+Postman做接口测试
自动化测试·软件测试·python·测试工具·测试用例·接口测试·postman
zckui2 小时前
conda常用命令
python·conda
General_G2 小时前
irobot_benchmark的编译和使用
linux·中间件·机器人·ros2
张3蜂2 小时前
Label-Studio图片标注初体验
python·开源