Python 列表与可变序列
Python 的 list 是存储对象引用的可变序列。它保持元素顺序,允许混合类型,并支持整数索引和切片操作。
names = ["Ada", "Grace", "Linus"]
first = names[0]
last = names[-1]
middle = names[1:2] # 返回一个新的浅拷贝列表
修改与别名(Aliasing)
在 Python 中,赋值操作只是将另一个变量名绑定到同一个列表对象,并不会复制列表内容。
original = [[1], [2]]
alias = original
shallow = original.copy()
alias.append([3]) # original 也会改变,因为指向同一对象
shallow[0].append(9) # 浅拷贝只复制外层,嵌套列表仍共享引用
除非你确实需要递归复制整个对象图(即深拷贝),否则不要滥用 copy.deepcopy。清晰的所有权模型通常比防御性的深拷贝更容易理解和维护。
常见的列表修改操作各有不同的语义:
items.append(value) # 在末尾追加单个值
items.extend(iterable) # 将可迭代对象中的所有元素追加到末尾
items.insert(index, value) # 在指定索引处插入值
last = items.pop() # 移除并返回末尾元素
items.remove(value) # 移除第一个匹配的值;若不存在则抛出 ValueError
items[1:3] = replacements # 切片赋值,可能会改变列表长度
警告:除非你明确知道自己在做什么,否则不要在遍历列表的同时修改其结构。更安全的做法是遍历列表的副本,或者构建一个新的结果列表。
推导式与生成器
对于简单的转换或过滤逻辑,列表推导式(List Comprehension)通常比 map/filter 更直观:
squares = [number * number for number in numbers if number >= 0]
注意,列表推导式会立即构建完整的列表并占用内存。如果数据量大且只需一次性消费,使用生成器表达式(Generator Expression)可以实现惰性求值,节省内存:
total = sum(number * number for number in numbers)
避免编写深层嵌套的推导式。对于涉及多步状态变化的逻辑,普通的 for 循环往往比复杂的推导式更清晰、更易调试。
典型时间复杂度
CPython 用可调整大小的引用数组实现列表,这是实现细节,不是所有 Python 实现都必须采用的内存布局。下表描述这一成本模型,并假定元素比较耗时为常数;自定义相等方法可能增加额外开销。
选型建议:
- 如果频繁在两端进行插入/删除操作,使用
collections.deque。 - 如果主要操作是成员判断或按键查找,使用
set或dict。 - 如果需要表示固定不变的序列,使用
tuple。但要注意:元组本身的不可变性并不意味着其引用的对象也是不可变的。
重复引用与原地操作的返回值
序列契约规定:重复运算重复的是引用,不会复制内部对象:
rows = [[0] * 2] * 3
rows[0][0] = 9
assert rows == [[9, 0], [9, 0], [9, 0]]
independent = [[0] * 2 for _ in range(3)]
independent[0][0] = 9
assert independent == [[9, 0], [0, 0], [0, 0]]
numbers = [3, 1, 2]
assert sorted(numbers) == [1, 2, 3]
assert numbers == [3, 1, 2]
assert numbers.sort() is None
assert numbers == [1, 2, 3]
alias = numbers
numbers += [4]
assert alias == [1, 2, 3, 4]
numbers = numbers + [5]
assert alias == [1, 2, 3, 4]
append、extend、reverse 和 sort 等方法修改原列表并返回 None,不要写 numbers = numbers.sort()。sorted 接受任意可迭代对象,返回新列表。两种排序都稳定:排序键相等的元素保留原有相对顺序。元素本身不能排序时,用 key= 选择可比较的属性。
索引空列表或对其调用 pop() 会抛出 IndexError;items[:10] 这样的切片则会裁剪边界。del items[index] 按位置删除,remove(value) 查找并移除第一个相等的值。步长不为一的扩展切片赋值要求替换项数量与选中的位置数量完全相同。