====== 第八章:列表与元组 ======
===== 本章目标 =====
完成本章学习后,你将能够:
* 掌握列表和元组的所有操作
* 理解可变与不可变的本质区别
* 熟练使用列表推导式
* 掌握切片的高级用法
* 选择合适的数据结构
===== 列表(List) =====
==== 列表创建 ====
# 直接创建
empty = []
numbers = [1, 2, 3, 4, 5]
mixed = [1, "hello", 3.14, True, None]
nested = [[1, 2], [3, 4], [5, 6]]
# 使用list()函数
chars = list("hello") # ['h', 'e', 'l', 'l', 'o']
nums = list(range(5)) # [0, 1, 2, 3, 4]
# 列表乘法
zeros = [0] * 10 # [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
# 注意:乘法对于可变对象的陷阱
lists = [[]] * 3
lists[0].append(1)
print(lists) # [[1], [1], [1]],三个引用同一个列表!
# 正确做法
lists = [[] for _ in range(3)]
lists[0].append(1)
print(lists) # [[1], [], []]
==== 列表索引与切片 ====
items = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
# 索引
print(items[0]) # 0
print(items[-1]) # 9(最后一个)
print(items[-2]) # 8(倒数第二个)
# 切片 [start:stop:step]
print(items[2:5]) # [2, 3, 4]
print(items[:3]) # [0, 1, 2]
print(items[7:]) # [7, 8, 9]
print(items[:]) # 副本
print(items[::2]) # [0, 2, 4, 6, 8](偶数索引)
print(items[1::2]) # [1, 3, 5, 7, 9](奇数索引)
print(items[::-1]) # [9, 8, 7, 6, 5, 4, 3, 2, 1, 0](反转)
# 切片赋值
items[2:5] = [20, 30] # 可以用不同长度的列表替换
print(items) # [0, 1, 20, 30, 5, 6, 7, 8, 9]
# 删除切片
del items[2:4]
print(items) # [0, 1, 5, 6, 7, 8, 9]
==== 列表方法 ====
# 添加元素
lst = [1, 2, 3]
lst.append(4) # [1, 2, 3, 4],在末尾添加
lst.insert(0, 0) # [0, 1, 2, 3, 4],在指定位置插入
lst.extend([5, 6]) # [0, 1, 2, 3, 4, 5, 6],扩展列表
# 删除元素
lst.remove(3) # 删除第一个值为3的元素
popped = lst.pop() # 删除并返回最后一个元素(6)
popped = lst.pop(0) # 删除并返回指定索引的元素(0)
del lst[0] # 删除指定索引
# 查找
lst = [1, 2, 3, 2, 4]
print(lst.index(2)) # 1,第一个2的位置
print(lst.index(2, 2)) # 3,从索引2开始找
print(lst.count(2)) # 2,2出现的次数
print(5 in lst) # False
# 排序
lst = [3, 1, 4, 1, 5, 9, 2, 6]
lst.sort() # 原地排序
lst.sort(reverse=True) # 降序
lst.sort(key=lambda x: -x) # 自定义排序
# 复制
lst = [1, 2, [3, 4]]
copy1 = lst.copy() # 浅拷贝
copy2 = lst[:] # 浅拷贝
copy3 = list(lst) # 浅拷贝
import copy
deep = copy.deepcopy(lst) # 深拷贝
# 反转
lst.reverse() # 原地反转
reversed_lst = lst[::-1] # 返回新列表
# 清空
lst.clear() # []
==== 列表作为栈和队列 ====
# 作为栈(LIFO - 后进先出)
stack = []
stack.append(1) # 入栈
stack.append(2)
stack.append(3)
top = stack[-1] # 查看栈顶
item = stack.pop() # 出栈,item = 3
# 作为队列(FIFO - 先进先出),但效率低
queue = []
queue.append(1) # 入队
queue.append(2)
item = queue.pop(0) # 出队,item = 1(O(n)操作,效率低!)
# 高效队列使用collections.deque
from collections import deque
d = deque()
d.append(1) # 右端添加
d.appendleft(0) # 左端添加
d.pop() # 右端移除
d.popleft() # 左端移除(O(1))
===== 元组(Tuple) =====
==== 元组创建 ====
# 直接创建
empty = ()
single = (1,) # 注意:必须有逗号!
not_tuple = (1) # 这只是整数1
numbers = (1, 2, 3)
# 括号可以省略
numbers = 1, 2, 3
a, b = 1, 2 # 元组解包
# 使用tuple()函数
chars = tuple("hello") # ('h', 'e', 'l', 'l', 'o')
nums = tuple([1, 2, 3]) # (1, 2, 3)
==== 元组不可变性 ====
t = (1, 2, 3)
# t[0] = 10 # TypeError: 'tuple' object does not support item assignment
# 但元组中的可变元素可以修改
t = ([1, 2], [3, 4])
t[0].append(3) # 可以!
print(t) # ([1, 2, 3], [3, 4])
# 重新赋值变量,不是修改元组
t = (1, 2, 3)
t = t + (4, 5) # 创建新元组
print(t) # (1, 2, 3, 4, 5)
==== 元组解包 ====
# 基本解包
coordinates = (3, 4)
x, y = coordinates
print(x, y) # 3 4
# 扩展解包(Python 3+)
first, *rest = [1, 2, 3, 4, 5]
print(first) # 1
print(rest) # [2, 3, 4, 5]
*beginning, last = [1, 2, 3, 4, 5]
print(beginning) # [1, 2, 3, 4]
print(last) # 5
first, *middle, last = [1, 2, 3, 4, 5]
print(middle) # [2, 3, 4]
# 忽略值
x, _, y = (1, 2, 3) # _常用于表示不关心的值
x, *_, y = (1, 2, 3, 4, 5) # _接收[2, 3, 4]
# 交换变量
a, b = 1, 2
a, b = b, a # 元组解包实现交换
print(a, b) # 2 1
==== 命名元组(Named Tuple)====
from collections import namedtuple
# 定义
Point = namedtuple('Point', ['x', 'y'])
Person = namedtuple('Person', 'name age city') # 也可以用空格分隔的字符串
# 创建实例
p = Point(3, 4)
alice = Person('Alice', 25, 'NYC')
# 访问
print(p.x, p.y) # 3 4
print(p[0], p[1]) # 3 4(也支持索引)
print(alice.name) # Alice
# 不可变
# p.x = 10 # AttributeError
# 转换为字典
print(alice._asdict())
# 替换字段(创建新实例)
alice2 = alice._replace(age=26)
===== 列表 vs 元组 =====
| 特性 | 列表 | 元组 |
| 语法 | [1, 2, 3] | (1, 2, 3) 或 1, 2, 3 |
| 可变性 | 可变 | 不可变 |
| 性能 | 稍慢 | 更快 |
| 内存 | 更多 | 更少 |
| 用途 | 需要修改的数据 | 固定数据、字典键 |
| 安全 | 可能被意外修改 | 更安全 |
import sys
import timeit
# 内存对比
lst = [1, 2, 3, 4, 5]
t = (1, 2, 3, 4, 5)
print(f"列表: {sys.getsizeof(lst)} bytes")
print(f"元组: {sys.getsizeof(t)} bytes")
# 性能对比
list_time = timeit.timeit("[1, 2, 3, 4, 5]", number=1000000)
tuple_time = timeit.timeit("(1, 2, 3, 4, 5)", number=1000000)
print(f"列表创建: {list_time:.4f}s")
print(f"元组创建: {tuple_time:.4f}s")
===== 本章练习 =====
1. **列表操作**:实现函数删除列表中所有指定值的元素
2. **矩阵转置**:不使用numpy,用列表推导式实现矩阵转置
3. **扁平化**:将任意深度的嵌套列表扁平化
4. **数据统计**:给定成绩列表,计算平均分、中位数、最高分、最低分
5. **排序算法**:实现快速排序或归并排序
===== 本章小结 =====
本章我们学习了:
* 列表的创建、索引、切片、方法
* 元组的特性和解包
* 命名元组的使用
* 列表与元组的选择
* 内存和性能考量
下一章:[[python_course:chapter09|第九章:字典与集合]]