Python列表切片高级技巧:原地修改、分页与slice对象
列表切片是 Python 很有代表性的语法。它不只是“取一段”,还能原地修改列表长度、反转、分页、批量赋值,并经常替代循环和边界判断。下面按语法规则、切片赋值、工程场景、slice 对象和性能陷阱展开。一、完整语法与默认值
lst =
print(lst) #
print(lst)#
语法为 lst。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、3、5;lst 取索引 6、4、2。
二、不越界、浅拷贝与 NumPy 视图差异
切片遇到越界不会报错:lst 自动截断,lst 返回空列表,而索引 lst 会抛 IndexError。边界模糊时优先用切片。切片创建浅拷贝:original[:] 是新列表,修改原列表不影响副本;嵌套列表的内层对象仍共享,需要 copy.deepcopy 才能完全独立。Python 列表切片返回新列表,不是视图;NumPy 数组切片是视图,修改 arr_view 会同步影响原数组。从列表切到 NumPy 时要特别注意这个兼容性差异。
三、切片赋值:原地修改列表
nums =
nums =
nums = # 列表可变长
nums = # 列表可变短
nums = [] # 删除一段
nums = # 在索引 1 处插入
nums[:0] = [-1, 0] # 开头插入
nums = # 末尾插入
带步长的切片赋值有严格限制:nums[::2] = 合法,因为右侧长度与切片位置数量一致;少一个或多一个会抛 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] 得到 。
旋转和交换可借助切片赋值原地完成:
def rotate_left(lst, k):
k %= len(lst)
lst[:] = lst + 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 + lst[:mid]
压缩列表:del lst[::2] 删除偶数索引位置,del lst 删除奇数索引位置,lst = [] 保留前 3 个和后 3 个。
五、工程场景:分页、截断、批量赋值与矩阵遍历
分页可直接用切片:
def paginate(items, page, page_size):
start = (page - 1) * page_size
end = start + page_size
return items
页码越界时切片返回空列表,不会抛异常。带信息分页可计算 total_pages = (total + page_size - 1) // page_size,再用 max/min 修正 page,返回 items 以及 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 = * 10
nums[:5] = * 5
nums[::2] = * 5
棋盘格可先构造 row_template[::2] 和 row_template[::-1],再按行奇偶复制。螺旋遍历矩阵则利用 matrix 去首行,再用 zip(*matrix) 转置并反转行,实现逆时针旋转:
def spiral_order(matrix):
result = []
while matrix:
result.extend(matrix)
matrix = matrix
if matrix:
matrix = [::-1]
return result
六、slice 对象与自定义类
slice(start, stop, step) 可以创建可复用的切片对象。s = slice(2, 8, 2) 后,lst 与 lst 完全一样。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 = for i in range(start, stop, step)]
return DataSeries(result)
elif isinstance(key, int):
return self._data
raise TypeError('不支持的索引类型:' + str(type(key)))
def __setitem__(self, key, value):
self._data = value
def __delitem__(self, key):
del self._data
def __len__(self):
return len(self._data)
七、性能与陷阱
切片会创建新列表,内存消耗 O(n);原地反转 reverse() 额外内存 O(1),reversed() 是懒迭代。切片不越界,但索引越界会 IndexError。浅拷贝不等于深拷贝,嵌套对象会共享。Python 列表切片不是视图,NumPy 切片是视图,混用容易误改数据。带步长切片赋值长度必须匹配;step 不能为 0。分页函数要处理 page 越界,但切片本身返回空列表,无需异常捕获。
八、小结
切片的核心价值是边界安全、语法紧凑、能原地修改列表长度,并能通过 slice 对象复用规则。掌握默认值、步长方向、浅拷贝、NumPy 视图差异之后,可以在分页、文本截断、批量初始化、矩阵遍历和自定义容器中减少循环与边界判断。
Re: Python列表切片高级技巧:原地修改、分页与slice对象
这篇总结很系统,尤其把切片不越界、浅拷贝和 NumPy 视图差异放在一起讲,确实很容易踩坑。切片赋值那段也实用,步长不为 1 时右侧长度必须匹配,步长为 1 才能自由变长变短,这个限制以前写代码时经常忘。分页、文本截断、rotate 和 swap_halves 这些例子都能直接套用,用 lst[:] = ... 原地改列表也很巧。第六部分好像刚开了个头,slice 对象和自定义类那块如果继续展开,应该也很有价值。Re: Python列表切片高级技巧:原地修改、分页与slice对象
总结得很系统,切片赋值和分页这两块特别实用。我之前只知道切片能取一段,没想到 nums = ... 可以当插入用,步长赋值必须等长这个坑也讲清楚了。旋转、交换、删奇偶索引那几段写法很干净,分页越界返回空列表这点确实比手动判断省心。第六部分好像没贴完,期待把 slice 对象和自定义类支持切片的内容补上。Re: Python列表切片高级技巧:原地修改、分页与slice对象
这帖整理得很细,尤其切片赋值能变长、变短这点,很多人只记得切片是“取一段”。我自己写分页时最常用就是 items,越界直接返回空列表,省掉不少 if 判断,再配合 total_pages 计算确实很省心。 有两个小地方值得注意: 一是切片返回的是浅拷贝,分页或截断大数据时如果直接返回 items,会复制一份新列表;如果元素是嵌套对象,内层还是共享引用,要完全独立还得 deepcopy。 二是 truncate 里 text[:max_length - len(ellipsis)],如果 max_length 比省略号还短,负索引会从尾部切,最好先保证 max_length 不小于 ellipsis 长度。 旋转那几个用 lst[:] = ... 的原地写法很实用,代码短,而且不会改变原列表对象的身份。第六节 slice 对象与自定义类好像没发完,期待补全,特别是 __getitem__ 里怎么处理 slice 参数。
页:
[1]