Day 08:【99天精通Python】列表推导式与元组 - 进阶技巧与不可变序列
前言
欢迎来到第8天!
在昨天的课程中,我们掌握了Python中最常用的数据结构——列表(List)的基础用法。你可能已经发现,用for循环来处理列表虽然直观,但有时代码会显得比较拖沓。
Python以"优雅"和"简洁"著称,今天我们将学习列表推导式,这是一种能让你用一行代码搞定复杂列表操作的"魔法"。此外,我们还将介绍列表的"孪生兄弟"——元组(Tuple),看看为什么我们需要一个"不可变"的列表。
本节内容:
- 列表推导式(List Comprehensions)
- 元组的创建与操作
- 元组的解包(Unpacking)
- 列表 vs 元组:如何选择?
- 实战练习
一、列表推导式:Pythonic的代码风格
1.1 什么是列表推导式?
列表推导式(List Comprehension)是Python提供的一种简洁的创建列表的方法。它可以将多行循环代码压缩成一行,不仅代码更少,而且执行效率往往更高。
场景对比:我们要创建一个包含1到10平方的列表。
# 普通写法(3行代码)squares=[]forxinrange(1,11):squares.append(x**2)print(squares)# [1, 4, 9, 16, 25, 36, 49, 64, 81, 100]# 列表推导式写法(1行代码)squares=[x**2forxinrange(1,11)]print(squares)1.2 基本语法
[expressionforiteminiterable]expression:对每个元素执行的操作(生成的元素)。item:从序列中取出的元素。iterable:可迭代对象(如列表、range等)。
1.3 带条件的列表推导式
我们可以在后面加上if语句进行过滤。
语法:
[expressionforiteminiterableifcondition]示例:筛选出1到10之间的偶数,并计算它们的平方。
# 普通写法evens=[]forxinrange(1,11):ifx%2==0:evens.append(x**2)# 列表推导式evens=[x**2forxinrange(1,11)ifx%2==0]print(evens)# [4, 16, 36, 64, 100]1.4 带if-else的列表推导式
注意:当需要else时,if语句必须放在for之前。
示例:将列表中的偶数保留,奇数变为负数。
numbers=[1,2,3,4,5]# [结果1 if 条件 else 结果2 for 变量 in 列表]new_nums=[xifx%2==0else-xforxinnumbers]print(new_nums)# [-1, 2, -3, 4, -5]1.5 嵌套列表推导式
推导式也可以嵌套,用于处理二维列表。
示例:将一个 3x3 的矩阵展平为一维列表。
matrix=[[1,2,3],[4,5,6],[7,8,9]]# 普通写法flattened=[]forrowinmatrix:fornuminrow:flattened.append(num)# 推导式写法flattened=[numforrowinmatrixfornuminrow]print(flattened)# [1, 2, 3, 4, 5, 6, 7, 8, 9]注意:虽然列表推导式很强大,但如果逻辑过分复杂(超过两层循环或复杂判断),为了代码的可读性,建议还是使用普通的
for循环。
二、元组(Tuple):不可变的列表
2.1 什么是元组?
元组与列表非常相似,它们都是有序的元素集合。最大的区别在于:元组是不可变的(Immutable)。一旦创建,就不能修改(不能增、删、改元素)。
元组使用小括号()表示。
2.2 创建元组
# 创建多个元素的元组t1=(1,2,3)t2=("a","b","c")t3=(1,"hello",3.14)# 混合类型# 省略括号(Python会自动打包)t4=1,2,3print(type(t4))# <class 'tuple'># 创建空元组empty=()# 【重要】创建只有一个元素的元组(必须加逗号)single_wrong=(1)# 这是整数 1single_right=(1,)# 这是元组 (1,)print(type(single_wrong))# <class 'int'>print(type(single_right))# <class 'tuple'>2.3 访问元组
访问方式与列表完全一致(索引和切片)。
fruits=("苹果","香蕉","橙子","葡萄")print(fruits[0])# 苹果print(fruits[-1])# 葡萄print(fruits[1:3])# ('香蕉', '橙子')2.4 元组的不可变性
t=(1,2,3)# 尝试修改会报错# t[0] = 10 # TypeError: 'tuple' object does not support item assignment# 尝试添加/删除也会报错# t.append(4) # AttributeError# del t[0] # TypeError特例:如果元组中包含可变对象(如列表),那么这个列表的内容是可以修改的。
t_mutable=(1,2,["a","b"])t_mutable[2][0]="X"print(t_mutable)# (1, 2, ['X', 'b']) -> 元组本身没变(引用的地址没变),但引用的列表内容变了三、元组的黑科技:打包与解包
3.1 序列解包(Unpacking)
我们可以将元组(或列表)中的元素一次性赋值给多个变量。
coordinates=(10,20)# 解包x,y=coordinatesprint(x)# 10print(y)# 20# 变量交换(利用解包原理)a=1b=2a,b=b,aprint(a,b)# 2 13.2 使用*处理剩余元素
如果变量数量少于元素数量,可以使用*收集剩余元素(结果为列表)。
numbers=(1,2,3,4,5)# 获取首尾,中间打包head,*middle,tail=numbersprint(head)# 1print(middle)# [2, 3, 4]print(tail)# 5# 只获取前两个a,b,*rest=numbersprint(a,b)# 1 2print(rest)# [3, 4, 5]四、列表 vs 元组
| 特性 | 列表 (List) | 元组 (Tuple) |
|---|---|---|
| 可变性 | 可变 (Mutable) | 不可变 (Immutable) |
| 符号 | [] | () |
| 速度 | 稍慢 | 稍快(内存占用更小) |
| 用途 | 数据需要频繁修改时 | 数据固定不变、作为字典键值、函数返回多个值 |
| 方法 | 丰富 (append, remove…) | 很少 (count, index) |
什么时候用元组?
- 数据保护:如果你传递一组数据给函数,且不希望函数修改它,用元组。
- 作为字典的键:列表不能做字典的键(因为可变),但元组可以。
- 函数返回值:函数返回多个值时,本质上是返回一个元组。
五、实战练习
练习1:列表推导式练手
使用列表推导式完成以下任务:
- 生成一个包含 1 到 20 之间所有能被 3 整除的数的列表。
- 有一个名字列表
names = ["Alice", "Bob", "Charlie", "David"],生成一个新列表,包含每个名字的长度。
# 1. 被3整除nums=[xforxinrange(1,21)ifx%3==0]print(nums)# [3, 6, 9, 12, 15, 18]# 2. 名字长度names=["Alice","Bob","Charlie","David"]name_lengths=[len(name)fornameinnames]print(name_lengths)# [5, 3, 7, 5]练习2:成绩分析(元组应用)
学生成绩以元组形式存储:(姓名, 分数)。
请筛选出及格(>=60)的学生姓名。
students=[("小明",85),("小红",59),("小刚",90),("小强",55)]# 使用列表推导式 + 解包passed_names=[nameforname,scoreinstudentsifscore>=60]print(passed_names)# ['小明', '小刚']练习3:找出两个列表的共同元素
使用列表推导式找出两个列表中都存在的元素。
list1=[1,2,3,4,5]list2=[4,5,6,7,8]common=[xforxinlist1ifxinlist2]print(common)# [4, 5]六、常见问题
Q1:元组只有一个元素时为什么要加逗号?
Python中的小括号()既用于元组,也用于数学运算优先级(如(1+2)*3)。如果不加逗号,Python会默认将其解析为数学运算括号或普通对象,而不是元组。
Q2:列表推导式一定会比for循环快吗?
在大多数情况下,列表推导式比等效的for循环要快,因为它是底层的C语言实现的优化。但是,如果逻辑极其复杂,推导式可能会牺牲可读性。可读性优先是Python的哲学。
七、小结
关键要点:
- 列表推导式是用一行代码生成列表的利器。
- 元组是不可变的列表,创建单元素元组记得加逗号
(1,)。 - 解包允许我们将序列快速赋值给多个变量,
*可以收集剩余元素。 - 在数据不需要修改的场景下,优先选择元组。
八、课后作业
- 筛选单词:给定一个句子字符串,将其转换为单词列表,然后使用列表推导式筛选出长度大于3的单词。
- 输入:“Python is an amazing language”
- 输出:[‘Python’, ‘amazing’, ‘language’]
- 元组统计:创建一个包含20个随机整数(1-10)的元组,统计数字 5 出现了多少次,并找出它第一次出现的索引。
- 坐标转换:有一个包含多个坐标元组的列表
points = [(1, 2), (3, 4), (5, 6)],请使用列表推导式生成一个新的列表,其中每个坐标的 x 和 y 互换位置(即变为[(2, 1), (4, 3), (6, 5)])。
下节预告
Day 09:字典(Dictionary)- Python中最强大的键值对存储结构,我们将解锁数据查询的新姿势!
系列导航:
- 上一篇:Day 07 - 列表基础
- 下一篇:Day 09 - 字典与集合(待更新)