一、列表

1. 什么是列表?

列表是一个有序可变允许重复元素的容器。在CPython(标准Python解释器)中,列表底层是一个动态数组(确切地说是PyObject*指针数组),这意味着它存储的是对象的引用,而不是对象本身。

特性总结:

  • 异构性:一个列表中可以同时存放整数、字符串、列表甚至函数。

  • 动态性:长度不固定,自动扩容。

  • 序列化:支持索引、切片、拼接等序列操作。


2. 列表的创建

除了用方括号,还有几种构建方式:

# 最常用
a = [1, 2, 3]

# 列表推导式(Pythonic 首选)
squares = [x**2 for x in range(10)]

# 类型构造器
b = list("hello")  # ['h', 'e', 'l', 'l', 'o']

# 生成重复列表(注意:如果是可变对象,会有坑!)
zeros = [0] * 5  # [0, 0, 0, 0, 0]

⚠️ 危险警告[[]] * 3 创建的是3个指向同一个空列表的引用。修改其中一个,其他两个也会变。正确做法是 [[] for _ in range(3)]

❌ 致命错误:千万不能用 [[0] * cols] * rows

这是网上最常见的错误写法,看似简洁,实则全是坑

# 错误示范!!!
matrix = [[0] * 4] * 3

# 看起来没问题
print(matrix)  # [[0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0]]

# 但当你修改第一行第一列时...
matrix[0][0] = 999

# 你会发现所有行的第一列都变成了 999!!!
print(matrix)  
# 输出:[[999, 0, 0, 0], [999, 0, 0, 0], [999, 0, 0, 0]]

3. 核心增删改查方法及时间复杂度

了解复杂度有助于你在高并发或大数据场景下做对的选择。

操作 方法 平均时间复杂度 备注
末尾追加 list.append(x) O(1) 极快
任意位置插入 list.insert(i, x) O(n) 插入位置之后的元素要后移
末尾删除 list.pop() O(1) 极快
任意位置删除 list.pop(i) 或 del O(n) 删除后元素要前移
按值删除 list.remove(x) O(n) 先遍历查找,再删除
查找索引 list.index(x) O(n) 线性扫描
成员判断 x in list O(n) 线性扫描
排序 list.sort() O(n log n) 原地排序,优化极好

4. 切片:被低估的杀手锏

切片不仅仅是取数据,它还能用来替换删除

nums = [0, 1, 2, 3, 4, 5]

# 取偶数下标
print(nums[::2])  # [0, 2, 4]

# 反转(经典写法)
print(nums[::-1])  # [5, 4, 3, 2, 1, 0]

# 用切片批量替换(长度可以不一致)
nums[1:4] = ['a', 'b']  # 把1,2,3替换成a,b,列表变短了

5. 列表作为栈和队列

  • 栈(LIFO):用 append() 入栈,pop() 出栈,完美契合。

  • 队列(FIFO):用 append() 入队,pop(0) 出队。但是pop(0) 是O(n)操作,如果数据量大,请改用 collections.deque


6. 深拷贝 vs 浅拷贝

这是初级程序员最容易踩的坑。

import copy

original = [[1, 2], [3, 4]]

# 浅拷贝:只拷贝外层,内层列表是同一个引用
shallow = original.copy()  
shallow[0][0] = 999
print(original)  # [[999, 2], [3, 4]] 原列表被改了!

# 深拷贝:递归拷贝所有层
deep = copy.deepcopy(original)  
deep[0][0] = 0
print(original)  # [[999, 2], [3, 4]] 原列表纹丝不动

7. 总结与建议

  1. 优先使用列表推导式,比 for 循环加上 append 更快(底层C循环)。

  2. 大数据量查询,考虑将列表转为 set 或 dict,用空间换时间。

  3. 频繁在头部插入/删除,请使用 deque

  4. 多维数组计算(如矩阵),请使用 NumPy,而不是嵌套列表。

二、列表推导式

1.列表推导式是一种基于现有可迭代对象快速创建新列表的语法糖。

它的核心思想是:"我要创建一个新列表,每个元素都是对原元素的某种转换或筛选"

# 传统写法(3行)
squares = []
for x in range(10):
    squares.append(x**2)

# 列表推导式(1行)
squares = [x**2 for x in range(10)]

2.基本语法结构

列表推导式的标准模板:

[expression for item in iterable if condition]

拆解成三部分:

  • expression:对每个元素执行的操作(可以是计算、函数调用等)

  • for item in iterable:遍历源数据

  • if condition(可选):过滤条件,只有满足条件的元素才进入新列表


3.从简单到复杂的7个实战案例

案例1:基础转换
# 将字符串列表转为大写
names = ["alice", "bob", "charlie"]
upper_names = [name.upper() for name in names]
# 输出:['ALICE', 'BOB', 'CHARLIE']

# 计算平方
nums = [1, 2, 3, 4, 5]
squares = [x**2 for x in nums]
# 输出:[1, 4, 9, 16, 25]
案例2:带条件的筛选
# 只取偶数
nums = range(20)
evens = [x for x in nums if x % 2 == 0]
# 输出:[0, 2, 4, 6, 8, 10, 12, 14, 16, 18]

# 过滤掉空字符串
texts = ["hello", "", "world", None, "python"]
cleaned = [s for s in texts if s]  # 非空字符串为True
# 输出:['hello', 'world', 'python']
案例3:if-else 条件表达式(注意位置!)

关键区别

  • if 在 for 后面 → 过滤(决定是否保留)

  • if-else 在 for 前面 → 三目运算(决定元素的值)

# 过滤:只保留正数
nums = [-3, -2, -1, 0, 1, 2, 3]
positive = [x for x in nums if x > 0]
# 输出:[1, 2, 3]

# 三目运算:正数不变,负数转为0
processed = [x if x > 0 else 0 for x in nums]
# 输出:[0, 0, 0, 0, 1, 2, 3]

# 更复杂的例子:奇偶分类
labels = ["偶数" if x % 2 == 0 else "奇数" for x in range(5)]
# 输出:['偶数', '奇数', '偶数', '奇数', '偶数']
四、嵌套循环的列表推导式

场景:扁平化二维列表

matrix = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]

# 传统写法
flat = []
for row in matrix:
    for num in row:
        flat.append(num)

# 列表推导式(顺序与嵌套for循环一致)
flat = [num for row in matrix for num in row]
# 输出:[1, 2, 3, 4, 5, 6, 7, 8, 9]

记忆技巧:把推导式中的 for 顺序想象成普通循环的外层到内层的顺序。

场景:生成坐标对

# 传统写法
pairs = []
for x in [1, 2, 3]:
    for y in ['a', 'b']:
        pairs.append((x, y))

# 列表推导式
pairs = [(x, y) for x in [1, 2, 3] for y in ['a', 'b']]
# 输出:[(1, 'a'), (1, 'b'), (2, 'a'), (2, 'b'), (3, 'a'), (3, 'b')]

场景:带条件的嵌套循环

# 找出两个列表中所有可能的组合,但排除相同的数字
list1 = [1, 2, 3]
list2 = [2, 3, 4]
result = [(a, b) for a in list1 for b in list2 if a != b]
# 输出:[(1, 2), (1, 3), (1, 4), (2, 3), (2, 4), (3, 2), (3, 4)]

4、实战案例:列表推导式的优雅应用

案例1:文本处理
text = "Hello, World! How are you today?"

# 提取所有单词(去掉标点,转小写)
import string
words = [word.strip(string.punctuation).lower() 
         for word in text.split()]
# 输出:['hello', 'world', 'how', 'are', 'you', 'today']

# 找出长度大于3的单词
long_words = [word for word in words if len(word) > 3]
# 输出:['hello', 'world', 'today']
案例2:数据清洗
data = [1, 2, '3', '4', None, 5, 'six', 0]

# 安全地提取所有整数
integers = [x for x in data if isinstance(x, int) and x > 0]
# 输出:[1, 2, 5]

# 转为字符串,空值填 'N/A'
cleaned = [str(x) if x is not None else 'N/A' for x in data]
# 输出:['1', '2', '3', '4', 'N/A', '5', 'six', '0']

案例3:矩阵转置

matrix = [
    [1, 2, 3],
    [4, 5, 6],
    [7, 8, 9]
]

# 转置(行列互换)
transposed = [[row[i] for row in matrix] for i in range(len(matrix[0]))]
# 输出:[[1, 4, 7], [2, 5, 8], [3, 6, 9]]

总结:什么时候该用列表推导式?

场景 是否使用 原因
简单的转换(如 [x*2 for x in list] ✅ 推荐 简洁、快速
带一个简单条件的过滤 ✅ 推荐 一行搞定
嵌套循环(最多2层) ✅ 可以 注意可读性
超过2层嵌套 ❌ 不推荐 可读性太差
逻辑复杂或有副作用 ❌ 不推荐 用普通循环更清晰
需要多次访问结果 ✅ 推荐 列表推导式合适
只需遍历一次且数据量大 ❌ 不推荐 改用生成器表达式

核心口诀

转换用推导,过滤加if,复杂写循环,大数用生成器。

列表推导式是Python优雅哲学的完美体现:简洁但不简单,强大但不复杂。掌握它,你的代码会更具Python风格(Pythonic),可读性和效率都会大幅提升!

Logo

Agent 垂直技术社区,欢迎活跃、内容共建。

更多推荐