列表创建与索引
列表是 Python 中最常用的可变序列类型,能够容纳任意类型的对象,并且支持动态增删。理解列表的创建方式和索引机制,是掌握 Python 数据结构的基础。
创建列表
最直接的方式是使用方括号字面量,元素之间用逗号分隔:
numbers = [1, 2, 3, 4, 5]
mixed = [1, "hello", 3.14, True, None] # 类型可以混用
nested = [[1, 2], [3, 4], [5, 6]] # 嵌套列表
也可以通过 list() 构造函数从其他可迭代对象创建列表。字符串会被拆成单个字符,range 对象会展开为具体数字:
chars = list("abc") # ['a', 'b', 'c']
digits = list(range(5)) # [0, 1, 2, 3, 4]
empty = list() # []
创建空列表有两种等价写法:[] 和 list()。对于已知元素的列表,字面量写法更简洁;当需要转换生成器、元组或字典键时,list() 构造函数必不可少。
索引访问
列表中的每个元素都有唯一的整数索引,从 0 开始计数。正向索引从左往右,负向索引从右往左:
fruits = ["apple", "banana", "cherry", "date"]
fruits[0] # 'apple',第一个元素
fruits[2] # 'cherry',第三个元素
fruits[-1] # 'date',最后一个元素
fruits[-2] # 'cherry',倒数第二个
索引访问在越界时会立即抛出 IndexError,这与切片的容错行为形成鲜明对比:
fruits[10] # IndexError: list index out of range
fruits[-10] # IndexError: list index out of range
切片操作
切片通过 [start:stop:step] 语法提取子序列。start 包含在内,stop 不包含,即左闭右开区间。三个参数均可省略:省略 start 默认为 0,省略 stop 默认为列表长度,省略 step 默认为 1:
nums = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
nums[2:5] # [2, 3, 4],索引2到4
nums[:4] # [0, 1, 2, 3],从头开始
nums[6:] # [6, 7, 8, 9],到末尾结束
nums[:] # [0, 1, 2, 3, 4, 5, 6, 7, 8, 9],完整拷贝
nums[::2] # [0, 2, 4, 6, 8],每隔一个取一个
nums[1::2] # [1, 3, 5, 7, 9],从1开始隔一个取
切片具有容错性:索引超出范围不会报错,而是自动截断到有效区间:
nums[5:100] # [5, 6, 7, 8, 9],不会报错
nums[20:30] # [],返回空列表
nums[-100:2] # [0, 1],负数过大也被截断
负步长切片可以实现反转效果,这是 Python 中反转序列最惯用的写法:
nums[::-1] # [9, 8, 7, 6, 5, 4, 3, 2, 1, 0],完全反转
nums[8:2:-1] # [8, 7, 6, 5, 4, 3],从8倒着取到3
nums[2:8:-1] # [],start < stop 且步长为负,结果为空
注意 nums[8:2:-1] 与 nums[2:8:-1] 的区别:前者 start > stop 配合负步长,能正常提取;后者方向矛盾,结果为空列表。
切片赋值
切片不仅可以读取,还可以用于赋值,这是修改列表最灵活的方式之一。赋值右侧必须是可迭代对象:
nums = [0, 1, 2, 3, 4, 5]
# 替换一段
nums[1:4] = [10, 20, 30]
# nums 变为 [0, 10, 20, 30, 4, 5]
# 插入元素(用长度不同的可迭代对象)
nums[2:2] = [99, 88] # 在索引2处插入
# nums 变为 [0, 10, 99, 88, 20, 30, 4, 5]
# 删除元素(赋值为空列表)
nums[3:5] = []
# nums 变为 [0, 10, 99, 30, 4, 5]
# 清空整个列表
nums[:] = []
# nums 变为 []
切片赋值会原地修改列表,不会创建新列表对象。nums[:] 与 nums 的区别在于,前者保持原列表对象的标识不变,只是清空内容;后者让变量指向一个全新的空列表。
带步长的切片赋值要求右侧元素数量与切片选中的元素数量严格相等,否则会报错:
nums = [0, 1, 2, 3, 4, 5]
nums[::2] = [10, 20, 30] # 选中 0,2,4 共3个,右侧也是3个,成功
# nums 变为 [10, 1, 20, 3, 30, 5]
nums[::2] = [10, 20] # ValueError: 左侧选中3个,右侧只有2个
深浅拷贝
将列表赋值给新变量,只是让两个变量指向同一个对象,修改会互相影响:
a = [1, 2, 3]
b = a
b[0] = 99
print(a) # [99, 2, 3],a 也被改了
print(a is b) # True,同一对象
切片 [:] 或 list.copy() 创建的是浅拷贝:外层列表是新对象,但内部元素仍共享引用:
import copy
a = [[1, 2], [3, 4]]
b = a[:] # 浅拷贝
c = copy.deepcopy(a) # 深拷贝
b[0][0] = 99
print(a[0]) # [99, 2],浅拷贝内部仍共享
print(c[0]) # [1, 2],深拷贝完全独立
print(a is b) # False,外层是不同对象
对于一维列表且元素均为不可变类型时,浅拷贝已足够安全;一旦列表嵌套或包含可变对象,必须使用 copy.deepcopy() 才能彻底隔离。