在 Python 中,列表(List)是最常用的数据结构之一。它既可以存储任意类型的数据,又支持动态扩展。在日常开发中,很多逻辑都离不开列表操作。不过,一些看似简单的用法,往往隐藏着容易忽略的问题。
基础操作
- 增加元素
lst = [1, 2, 3]
lst.append(4) # 末尾添加
lst.insert(1, 10) # 指定位置插入
print(lst) # [1, 10, 2, 3, 4]
append:始终加在最后insert:需要指定索引位置
- 删除元素
lst = [1, 2, 3, 4]
a = lst.pop() # 删除最后一个
print(a) # 4
lst.pop(1) # 删除指定位置
print(lst) # [1, 3]
注意:pop 会返回被删除的元素,这在某些场景中很有用。
- 修改元素
lst = [1, 2, 3]
lst[0] = 100
print(lst) # [100, 2, 3]
注意:上一篇我们说到 list 是可变数据类型,内容可以被直接修改。
- 查询与切片
lst = [1, 2, 3, 4, 5]
print(lst[0]) # 1
print(lst[1:4]) # [2, 3, 4]
print(lst[::-1]) # 反转列表
| 切片形式 | 参数 | 含义 |
|---|---|---|
| Ist[i] | 第一个元素位置为0 | 访问单个元素 |
| Ist[start:stop] | start : 起始索引(包含)stop : 结束索引(不包含) | 从 start 到 stop-1 |
| Ist[start:stop:step] | start:若省略,默认为列表末尾;stop:若省略,默认为列表开头;step:若step为负数时,从右向左遍历,若为 -1,表示从后往前遍历 | 带step的切片 |
列表推导式
当你需要对列表做批量处理时,列表推导式会让代码更简洁。
lst = [1, 2, 3, 4]
# 每个元素平方
new_lst = [x * x for x in lst]
print(new_lst) # [1, 4, 9, 16]
带条件过滤,写法:[表达式 for 变量 in 可迭代对象 if 条件]
# 列表推导式,只保留偶数
even_lst = [x for x in lst if x % 2 == 0]
print(even_lst) # [2, 4]
# 传统写法
result = []
for x in lst:
if x % 2 == 0:
result.append(x)
列表推导式是Python的"语法糖",比传统循环快30-50%,代码也更优雅。
列表常见错误
- 复制列表
a = [1, 2, 3]
b = a # 不是复制,而是引用
b.append(4)
print(a) # [1, 2, 3, 4]
这里的 b = a 并没有创建新列表,而是指向同一块内存。
正确做法:
b = a.copy()
# 或
b = a[:]
- 嵌套列表的引用
lst = [[0] * 3] * 3
lst[0][0] = 1
print(lst) # [[1, 0, 0], [1, 0, 0], [1, 0, 0]]
上述代码拆解:
- [0] * 3 → 创建列表 [0, 0, 0]
- [[0] * 3] → 创建包含一个子列表的列表 [[0, 0, 0]]
- [[0] * 3] * 3 → 复制 3 次,得到 [[0, 0, 0], [0, 0, 0], [0, 0, 0]]
而* 运算符对列表进行复制时,是 浅拷贝 ,只复制引用,不创建新对象!即 lst[0] 、 lst[1] 、 lst[2] 都指向同一个列表对象。因此执行 lst[0][0] = 1 后所有子列表的第一个元素都会变成 1。
正确写法:
lst = [[0] * 3 for _ in range(3)] #循环只需要执行 3 次,不需要使用循环变量
lst[0][0] = 1
print(lst) # [[1, 0, 0], [0, 0, 0], [0, 0, 0]]
拆解:
- 第1次循环: [0] * 3 → 创建新列表 [0, 0, 0] → 存入 lst[0]
- 第2次循环: [0] * 3 → 创建新列表 [0, 0, 0] → 存入 lst[1]
- 第3次循环: [0] * 3 → 创建新列表 [0, 0, 0] → 存入 lst[2]
即,每次循环都会创建一个全新的列表对象,互不影响。
总结
列表看起来简单,但在复杂逻辑中,很容易因为引用关系引发问题。理解这一点,对后续学习数据结构和内存模型也有帮助。