列表切片是 Python 很有代表性的语法。它不只是“取一段”,还能原地修改列表长度、反转、分页、批量赋值,并经常替代循环和边界判断。下面按语法规则、切片赋值、工程场景、slice 对象和性能陷阱展开。
一、完整语法与默认值- lst = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
- print(lst[2:7:2]) # [2, 4, 6]
- print(lst[8:2:-2]) # [8, 6, 4]
复制代码 语法为 lst[start:stop:step]。start 包含,stop 不包含,step 默认 1 且不能为 0。step 大于 0 时,start 默认 0,stop 默认 len(lst);step 小于 0 时,start 默认 -1(最后一个元素),stop 默认 -len(lst)-1,所以 lst[::-1] 与 lst[-1:-11:-1] 等价。切片按 start、start+step、start+2*step……直到达到或越过 stop。例如 lst[1:6:2] 取索引 1、3、5;lst[6:1:-2] 取索引 6、4、2。
二、不越界、浅拷贝与 NumPy 视图差异
切片遇到越界不会报错:lst[0:100] 自动截断,lst[10:20] 返回空列表,而索引 lst[100] 会抛 IndexError。边界模糊时优先用切片。切片创建浅拷贝:original[:] 是新列表,修改原列表不影响副本;嵌套列表的内层对象仍共享,需要 copy.deepcopy 才能完全独立。Python 列表切片返回新列表,不是视图;NumPy 数组切片是视图,修改 arr_view 会同步影响原数组。从列表切到 NumPy 时要特别注意这个兼容性差异。
三、切片赋值:原地修改列表- nums = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
- nums[3:7] = [30, 40, 50, 60]
- nums[3:7] = [30, 40, 50, 60, 70, 80] # 列表可变长
- nums[3:9] = [300] # 列表可变短
- nums[2:5] = [] # 删除一段
- nums[1:1] = [2, 3, 4] # 在索引 1 处插入
- nums[:0] = [-1, 0] # 开头插入
- nums[len(nums):] = [10, 11] # 末尾插入
复制代码 带步长的切片赋值有严格限制:nums[::2] = [10, 20, 30] 合法,因为右侧长度与切片位置数量一致;少一个或多一个会抛 ValueError: attempt to assign sequence of size 2 to extended slice of size 3。步长为 1 的切片赋值不受此限,可以改变列表长度。删除时 del lst[::2] 与 lst[::2] = [] 效果相同。
四、常用技巧
反转序列:lst[::-1] 最常用但会创建新列表;list.reverse() 原地反转,额外内存 O(1);reversed(lst) 返回懒迭代器,同样 O(1) 额外内存。字符串也可用 text[::-1] 反转。
浅拷贝写法:original[:]、original.copy()、list(original)。实测 .copy() 通常最快,[:] 次之,list() 最慢。
降采样:lst[::factor],例如 list(range(100))[::10] 得到 [0, 10, 20, 30, 40, 50, 60, 70, 80, 90]。
旋转和交换可借助切片赋值原地完成:- def rotate_left(lst, k):
- k %= len(lst)
- lst[:] = lst[k:] + lst[:k]
- def rotate_right(lst, k):
- k %= len(lst)
- lst[:] = lst[-k:] + lst[:-k]
- def swap_halves(lst):
- mid = len(lst) // 2
- lst[:] = lst[mid:] + lst[:mid]
复制代码 压缩列表:del lst[::2] 删除偶数索引位置,del lst[1::2] 删除奇数索引位置,lst[3:-3] = [] 保留前 3 个和后 3 个。
五、工程场景:分页、截断、批量赋值与矩阵遍历
分页可直接用切片:- def paginate(items, page, page_size):
- start = (page - 1) * page_size
- end = start + page_size
- return items[start:end]
复制代码 页码越界时切片返回空列表,不会抛异常。带信息分页可计算 total_pages = (total + page_size - 1) // page_size,再用 max/min 修正 page,返回 items[start:end] 以及 has_prev、has_next。
文本截断:- def truncate(text, max_length, ellipsis='...'):
- if len(text) <= max_length:
- return text
- return text[:max_length - len(ellipsis)] + ellipsis
- def summary(text, n=100):
- return text[:n]
复制代码 尾部日志可用 lines = text.splitlines(),再取 lines[-n:]。
批量赋值:- nums = [0] * 10
- nums[:5] = [1] * 5
- nums[::2] = [1] * 5
复制代码 棋盘格可先构造 row_template[::2] 和 row_template[::-1],再按行奇偶复制。螺旋遍历矩阵则利用 matrix[1:] 去首行,再用 zip(*matrix) 转置并反转行,实现逆时针旋转:- def spiral_order(matrix):
- result = []
- while matrix:
- result.extend(matrix[0])
- matrix = matrix[1:]
- if matrix:
- matrix = [list(row) for row in zip(*matrix)][::-1]
- return result
复制代码
六、slice 对象与自定义类
slice(start, stop, step) 可以创建可复用的切片对象。s = slice(2, 8, 2) 后,lst 与 lst[2:8:2] 完全一样。slice 对象有 start、stop、step 属性,indices(len) 能把参数规范到合法范围:slice(5, 100, 2).indices(10) 得到 (5, 10, 2),slice(-100, 3, 1).indices(10) 得到 (0, 3, 1)。自定义类可实现 __getitem__、__setitem__、__delitem__ 来支持切片:- class DataSeries:
- def __init__(self, data):
- self._data = list(data)
- def __getitem__(self, key):
- if isinstance(key, slice):
- start, stop, step = key.indices(len(self._data))
- result = [self._data[i] for i in range(start, stop, step)]
- return DataSeries(result)
- elif isinstance(key, int):
- return self._data[key]
- raise TypeError('不支持的索引类型:' + str(type(key)))
- def __setitem__(self, key, value):
- self._data[key] = value
- def __delitem__(self, key):
- del self._data[key]
- def __len__(self):
- return len(self._data)
复制代码
七、性能与陷阱
切片会创建新列表,内存消耗 O(n);原地反转 reverse() 额外内存 O(1),reversed() 是懒迭代。切片不越界,但索引越界会 IndexError。浅拷贝不等于深拷贝,嵌套对象会共享。Python 列表切片不是视图,NumPy 切片是视图,混用容易误改数据。带步长切片赋值长度必须匹配;step 不能为 0。分页函数要处理 page 越界,但切片本身返回空列表,无需异常捕获。
八、小结
切片的核心价值是边界安全、语法紧凑、能原地修改列表长度,并能通过 slice 对象复用规则。掌握默认值、步长方向、浅拷贝、NumPy 视图差异之后,可以在分页、文本截断、批量初始化、矩阵遍历和自定义容器中减少循环与边界判断。 |