Python基礎(chǔ)指南之列表切片操作的高級(jí)應(yīng)用場(chǎng)景
一、開(kāi)篇:切片是Python列表的核武器
今天我們要重點(diǎn)突破Python中最強(qiáng)大、最獨(dú)特的特性之一——切片(Slice)。
切片操作可能是Python區(qū)別于其他編程語(yǔ)言最鮮明的特性之一。如果你用過(guò)Java或C++,你一定記得用循環(huán)提取子數(shù)組的繁瑣。而在Python中,一行 lst[2:8:2] 就能完成那些語(yǔ)言需要五六行代碼才能做到的事。
但切片遠(yuǎn)不止"取一段數(shù)據(jù)"這么簡(jiǎn)單。它可以原地修改列表的長(zhǎng)度、反轉(zhuǎn)序列、優(yōu)雅地處理邊界情況、實(shí)現(xiàn)高效的批量賦值。今天這篇文章,我會(huì)把切片的所有高級(jí)應(yīng)用場(chǎng)景一一拆解給你看。
二、切片的基礎(chǔ)回顧與深入理解
2.1 切片的完整語(yǔ)法
lst = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] # 完整語(yǔ)法:lst[start:stop:step] # start:起始索引(包含),默認(rèn):step>0時(shí)為0,step<0時(shí)為-1 # stop:結(jié)束索引(不包含),默認(rèn):step>0時(shí)為len(lst),step<0時(shí)為-len(lst)-1 # step:步長(zhǎng),默認(rèn)1(不能為0) # 從索引2到索引7(步長(zhǎng)為2) print(lst[2:7:2]) # [2, 4, 6] # 從索引8到索引2(反向步長(zhǎng)為2) print(lst[8:2:-2]) # [8, 6, 4]
2.2 切片默認(rèn)值的完整規(guī)則
lst = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] # 當(dāng)step > 0(正向)時(shí): # - start默認(rèn)值 = 0 # - stop默認(rèn)值 = len(lst) # 以下等價(jià): print(lst[0:10:1]) # [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] print(lst[::1]) # [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] print(lst[:]) # [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] print(lst[0::1]) # [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] print(lst[:10:1]) # [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] # 當(dāng)step < 0(反向)時(shí): # - start默認(rèn)值 = len(lst) - 1(即-1,最后一個(gè)元素) # - stop默認(rèn)值 = -len(lst) - 1(即取到最前面) # 以下等價(jià): print(lst[::-1]) # [9, 8, 7, 6, 5, 4, 3, 2, 1, 0] print(lst[-1:-11:-1]) # [9, 8, 7, 6, 5, 4, 3, 2, 1, 0]
2.3 切片參數(shù)的計(jì)算規(guī)則
# 理解切片中的索引計(jì)算 lst = [10, 20, 30, 40, 50, 60, 70, 80] # 索引:0 1 2 3 4 5 6 7 # 規(guī)則:lst[start:stop:step] 包含索引 start, start+step, start+2*step, ... # 直到達(dá)到或越過(guò) stop # 例:lst[1:6:2] # 索引序列:1, 1+2=3, 3+2=5 → 索引1, 3, 5 # 下一個(gè) 5+2=7 ≥ stop=6,停止 print(lst[1:6:2]) # [20, 40, 60] # 例:lst[6:1:-2] # 索引序列:6, 6-2=4, 4-2=2 → 索引6, 4, 2 # 下一個(gè) 2-2=0 ≤ stop=1,停止 print(lst[6:1:-2]) # [70, 50, 30] # 核心記憶:step的正負(fù)決定方向,包含start不包含stop
三、切片的強(qiáng)大特性
3.1 切片不越界——這是最實(shí)用的特性
lst = [1, 2, 3, 4, 5] # 無(wú)論start和stop有多大或多小,切片都不會(huì)報(bào)錯(cuò) print(lst[0:100]) # [1, 2, 3, 4, 5](自動(dòng)截?cái)啵? print(lst[-100:3]) # [1, 2, 3] print(lst[10:20]) # [](start超出范圍,返回空列表) print(lst[-10:-20]) # [](空范圍,返回空列表) print(lst[100:0]) # [](start > stop,返回空列表) # 這就是為什么切片比索引更適合處理邊界模糊的情況 # 索引訪問(wèn):越界就報(bào)錯(cuò) # lst[100] # IndexError # 切片訪問(wèn):越界就截?cái)嗷蚍祷乜?,永不出錯(cuò) # 這個(gè)特性讓很多邊界處理的代碼變簡(jiǎn)單了
3.2 切片創(chuàng)建的是淺拷貝
original = [1, 2, 3] sliced = original[:] # 創(chuàng)建副本 # 修改原列表不影響切片 original[0] = 999 print(original) # [999, 2, 3] print(sliced) # [1, 2, 3] # 但對(duì)于嵌套列表,內(nèi)層對(duì)象是共享的 nested = [[1, 2], [3, 4]] copy_nested = nested[:] nested[0][0] = 999 print(nested) # [[999, 2], [3, 4]] print(copy_nested) # [[999, 2], [3, 4]](內(nèi)層共享?。? # 如果需要完全獨(dú)立的副本,用copy.deepcopy import copy deep = copy.deepcopy(nested) nested[0][0] = 111 print(nested) # [[111, 2], [3, 4]] print(deep) # [[999, 2], [3, 4]](不受影響)
3.3 切片的獨(dú)立性——對(duì)切片賦值會(huì)影響原列表
# 用切片獲取一個(gè)視圖...等等,Python的切片返回的是新列表,不是視圖! # 這一點(diǎn)和NumPy不一樣 lst = [1, 2, 3, 4, 5] view = lst[1:4] # 這是一個(gè)新列表! view[0] = 999 print(lst) # [1, 2, 3, 4, 5](原列表不變) print(view) # [999, 3, 4] # 這一點(diǎn)和NumPy數(shù)組的行為不同 import numpy as np arr = np.array([1, 2, 3, 4, 5]) arr_view = arr[1:4] # NumPy的切片是視圖(共享底層數(shù)據(jù)) arr_view[0] = 999 print(arr) # [ 1 999 3 4 5](原數(shù)組也被改了?。? print(arr_view) # [999 3 4] # Python列表使用這種方式原地修改切片區(qū)域: lst[1:4] = [999, 999, 999] print(lst) # [1, 999, 999, 999, 5](原列表被修改了)
這是Python列表和NumPy數(shù)組的一個(gè)重要區(qū)別。Python列表的切片總是創(chuàng)建新列表,而NumPy的切片是視圖。在數(shù)據(jù)科學(xué)領(lǐng)域切換時(shí)特別注意。
四、用切片原地修改列表
4.1 切片賦值:替換任意范圍的元素
nums = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] # 替換中間一段 nums[3:7] = [30, 40, 50, 60] print(nums) # [0, 1, 2, 30, 40, 50, 60, 7, 8, 9] # 替換為更長(zhǎng)的序列——列表變長(zhǎng)! nums[3:7] = [30, 40, 50, 60, 70, 80] print(nums) # [0, 1, 2, 30, 40, 50, 60, 70, 80, 7, 8, 9] # 替換為更短的序列——列表變短! nums[3:9] = [300] print(nums) # [0, 1, 2, 300, 7, 8, 9] # 替換為空——?jiǎng)h除一段元素 nums[2:5] = [] print(nums) # [0, 1, 7, 8, 9]
4.2 在任意位置插入元素
nums = [1, 5, 9] # 在索引1處插入多個(gè)元素 nums[1:1] = [2, 3, 4] print(nums) # [1, 2, 3, 4, 5, 9] # 在開(kāi)頭插入 nums[:0] = [-1, 0] print(nums) # [-1, 0, 1, 2, 3, 4, 5, 9] # 在末尾插入 nums[len(nums):] = [10, 11] print(nums) # [-1, 0, 1, 2, 3, 4, 5, 9, 10, 11] # 在中間插入 mid = len(nums) // 2 nums[mid:mid] = ['中'] print(nums) # [-1, 0, 1, 2, 3, 4, '中', 5, 9, 10, 11]
4.3 刪除任意范圍的元素
nums = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] # 刪除前3個(gè) nums[:3] = [] print(nums) # [3, 4, 5, 6, 7, 8, 9] # 刪除后3個(gè) nums[-3:] = [] print(nums) # [3, 4, 5, 6] # 刪除偶數(shù)位置(每隔一個(gè)) nums[::2] = [] print(nums) # [4, 6] # 注意:`del lst[::2]` 和 `lst[::2] = []` 效果相同 nums = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] del nums[::2] print(nums) # [1, 3, 5, 7, 9]
4.4 帶步長(zhǎng)的切片賦值——有嚴(yán)格限制
nums = [0, 1, 2, 3, 4, 5] # 帶步長(zhǎng)的切片賦值:右邊的長(zhǎng)度必須和切片結(jié)果長(zhǎng)度一致! nums[::2] = [10, 20, 30] # 3個(gè)元素對(duì)3個(gè)位置 print(nums) # [10, 1, 20, 3, 30, 5] # ? 長(zhǎng)度不匹配會(huì)報(bào)錯(cuò) # nums[::2] = [10, 20] # ValueError: attempt to assign sequence of size 2 to extended slice of size 3 # nums[::2] = [10, 20, 30, 40] # ValueError # 這個(gè)限制只在步長(zhǎng)≠1時(shí)存在 # 步長(zhǎng)為1的切片(lst[a:b])賦值不受此限,可以改變列表長(zhǎng)度
五、切片的實(shí)用技巧大全
5.1 反轉(zhuǎn)序列
# 方式一:[::-1](最常用)
lst = [1, 2, 3, 4, 5]
reversed_lst = lst[::-1]
print(reversed_lst) # [5, 4, 3, 2, 1]
# 方式二:reversed()(返回迭代器,不創(chuàng)建新列表)
for item in reversed(lst):
print(item, end=' ') # 5 4 3 2 1
print()
# 方式三:list.reverse()(原地反轉(zhuǎn),不創(chuàng)建新列表)
lst.reverse()
print(lst) # [5, 4, 3, 2, 1]
# 性能對(duì)比
# lst[::-1]:創(chuàng)建新列表,O(n)內(nèi)存
# lst.reverse():原地反轉(zhuǎn),O(1)額外內(nèi)存
# reversed(lst):懶迭代,O(1)額外內(nèi)存
# 反向字符串
text = 'hello world'
print(text[::-1]) # dlrow olleh
5.2 淺拷貝的多種寫法
original = [1, 2, 3, 4, 5]
# 三種等價(jià)的淺拷貝方式
copy1 = original[:]
copy2 = original.copy()
copy3 = list(original)
# 它們創(chuàng)建的都是淺拷貝
print(copy1 == copy2 == copy3) # True
print(copy1 is original) # False
# 哪種最快?實(shí)際測(cè)試:
import time
n = 1000000
lst = list(range(100))
start = time.perf_counter()
for _ in range(n):
c = lst[:]
print(f'[:] {time.perf_counter() - start:.3f}秒')
start = time.perf_counter()
for _ in range(n):
c = lst.copy()
print(f'.copy() {time.perf_counter() - start:.3f}秒')
start = time.perf_counter()
for _ in range(n):
c = list(lst)
print(f'list() {time.perf_counter() - start:.3f}秒')
# .copy()通常最快(因?yàn)槭侵苯拥腃方法調(diào)用),[:]次之,list()最慢
5.3 每隔N個(gè)取一個(gè)元素
lst = list(range(30))
# 每隔2個(gè)取一個(gè)(所有偶數(shù)索引)
print(lst[::2])
# [0, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28]
# 每隔3個(gè)取一個(gè)
print(lst[::3])
# [0, 3, 6, 9, 12, 15, 18, 21, 24, 27]
# 從第2個(gè)開(kāi)始,每隔4個(gè)取一個(gè)
print(lst[1::4])
# [1, 5, 9, 13, 17, 21, 25, 29]
# 實(shí)際應(yīng)用:對(duì)列表進(jìn)行降采樣
def downsample(lst, factor):
"""將列表降采樣為原來(lái)的1/factor"""
return lst[::factor]
original = list(range(100))
reduced = downsample(original, 10)
print(reduced) # [0, 10, 20, 30, 40, 50, 60, 70, 80, 90]
5.4 批量交換元素位置
# 將列表的前半部分和后半部分互換
def swap_halves(lst):
mid = len(lst) // 2
# 注意:直接賦值會(huì)改變?cè)斜?
lst[:] = lst[mid:] + lst[:mid]
nums = [1, 2, 3, 4, 5, 6]
swap_halves(nums)
print(nums) # [4, 5, 6, 1, 2, 3]
# 循環(huán)左移k位
def rotate_left(lst, k):
k %= len(lst)
lst[:] = lst[k:] + lst[:k]
nums = [1, 2, 3, 4, 5, 6, 7]
rotate_left(nums, 2)
print(nums) # [3, 4, 5, 6, 7, 1, 2]
# 循環(huán)右移k位
def rotate_right(lst, k):
k %= len(lst)
lst[:] = lst[-k:] + lst[:-k]
nums = [1, 2, 3, 4, 5, 6, 7]
rotate_right(nums, 2)
print(nums) # [6, 7, 1, 2, 3, 4, 5]
5.5 列表"壓縮"——去除特定位置的元素
# 去除所有偶數(shù)索引位置的元素 lst = ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h'] del lst[::2] print(lst) # ['b', 'd', 'f', 'h'] # 去除所有奇數(shù)索引位置的元素 lst = ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h'] del lst[1::2] print(lst) # ['a', 'c', 'e', 'g'] # 保留前3個(gè)和后3個(gè),刪除中間的 lst = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] lst[3:-3] = [] print(lst) # [0, 1, 2, 7, 8, 9]
六、切片的高級(jí)應(yīng)用場(chǎng)景
6.1 數(shù)據(jù)分頁(yè)
def paginate(items, page, page_size):
"""用切片實(shí)現(xiàn)數(shù)據(jù)分頁(yè)"""
start = (page - 1) * page_size
end = start + page_size
return items[start:end]
# 模擬數(shù)據(jù)庫(kù)查詢結(jié)果
all_data = list(range(1, 101)) # 100條數(shù)據(jù)
page_size = 10
# 第1頁(yè)
print(f'第1頁(yè): {paginate(all_data, 1, page_size)}')
# 第3頁(yè)
print(f'第3頁(yè): {paginate(all_data, 3, page_size)}')
# 最后一頁(yè)(可能不滿一頁(yè))
print(f'第10頁(yè): {paginate(all_data, 10, page_size)}')
# 超出范圍返回空列表(不會(huì)報(bào)錯(cuò))
print(f'第20頁(yè): {paginate(all_data, 20, page_size)}') # []
def paginate_with_info(items, page, page_size):
"""帶分頁(yè)信息的分頁(yè)"""
total = len(items)
total_pages = (total + page_size - 1) // page_size
page = max(1, min(page, total_pages))
start = (page - 1) * page_size
end = min(start + page_size, total)
return {
'items': items[start:end],
'page': page,
'page_size': page_size,
'total': total,
'total_pages': total_pages,
'has_prev': page > 1,
'has_next': page < total_pages,
}
info = paginate_with_info(all_data, 1, 10)
print(f'第{info["page"]}頁(yè)/共{info["total_pages"]}頁(yè)')
print(f'數(shù)據(jù): {info["items"]}')
print(f'有上一頁(yè): {info["has_prev"]}, 有下一頁(yè): {info["has_next"]}')
6.2 文本處理和截?cái)?/h3>
# 截?cái)嚅L(zhǎng)文本,加省略號(hào)
def truncate(text, max_length, ellipsis='...'):
"""用切片實(shí)現(xiàn)文本截?cái)?""
if len(text) <= max_length:
return text
return text[:max_length - len(ellipsis)] + ellipsis
long_text = '這是一段很長(zhǎng)的文本內(nèi)容,我們需要將其截?cái)囡@示'
print(truncate(long_text, 15)) # 這是一段很長(zhǎng)的文本內(nèi)容,我...
# 提取摘要(取前N個(gè)字符)
def summary(text, n=100):
return text[:n]
# 取文本的最后N行
def last_n_lines(text, n):
lines = text.splitlines()
return '\n'.join(lines[-n:])
log = '\n'.join(f'第{i}行日志:一切正常' for i in range(1, 101))
print(last_n_lines(log, 3))
# 第98行日志:一切正常
# 第99行日志:一切正常
# 第100行日志:一切正常
# 截?cái)嚅L(zhǎng)文本,加省略號(hào)
def truncate(text, max_length, ellipsis='...'):
"""用切片實(shí)現(xiàn)文本截?cái)?""
if len(text) <= max_length:
return text
return text[:max_length - len(ellipsis)] + ellipsis
long_text = '這是一段很長(zhǎng)的文本內(nèi)容,我們需要將其截?cái)囡@示'
print(truncate(long_text, 15)) # 這是一段很長(zhǎng)的文本內(nèi)容,我...
# 提取摘要(取前N個(gè)字符)
def summary(text, n=100):
return text[:n]
# 取文本的最后N行
def last_n_lines(text, n):
lines = text.splitlines()
return '\n'.join(lines[-n:])
log = '\n'.join(f'第{i}行日志:一切正常' for i in range(1, 101))
print(last_n_lines(log, 3))
# 第98行日志:一切正常
# 第99行日志:一切正常
# 第100行日志:一切正常
6.3 批量賦值模式
# 初始化部分元素
nums = [0] * 10
# 將前5個(gè)設(shè)為1
nums[:5] = [1] * 5
print(nums) # [1, 1, 1, 1, 1, 0, 0, 0, 0, 0]
# 交替模式
nums = [0] * 10
nums[::2] = [1] * 5 # 偶數(shù)位為1
print(nums) # [1, 0, 1, 0, 1, 0, 1, 0, 1, 0]
# 更復(fù)雜的模式——棋盤格
size = 8
row_template = [0] * size
row_template[::2] = [1] * (size // 2)
# row_template = [1, 0, 1, 0, 1, 0, 1, 0]
board = []
for i in range(size):
if i % 2 == 0:
board.append(row_template[:])
else:
board.append(row_template[::-1])
for row in board:
print(' '.join('█' if c else ' ' for c in row))
6.4 螺旋遍歷矩陣
def spiral_order(matrix):
"""用切片實(shí)現(xiàn)矩陣的螺旋遍歷"""
result = []
while matrix:
# 取第一行
result.extend(matrix[0])
# 去掉第一行,然后逆時(shí)針旋轉(zhuǎn)矩陣
matrix = matrix[1:]
if matrix:
# 逆時(shí)針旋轉(zhuǎn) = 轉(zhuǎn)置 + 行反轉(zhuǎn)
matrix = [list(row) for row in zip(*matrix)][::-1]
return result
matrix = [
[1, 2, 3, 4],
[5, 6, 7, 8],
[9, 10, 11, 12],
]
print(spiral_order(matrix))
# [1, 2, 3, 4, 8, 12, 11, 10, 9, 5, 6, 7]
七、切片對(duì)象——可復(fù)用的切片
7.1 使用slice()創(chuàng)建切片對(duì)象
# slice(start, stop, step)創(chuàng)建切片對(duì)象 s = slice(2, 8, 2) print(s) # slice(2, 8, 2) lst = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] print(lst[s]) # [2, 4, 6](和lst[2:8:2]完全一樣) # 切片對(duì)象可以復(fù)用 data1 = list(range(100)) data2 = list(range(100, 200)) data3 = list(range(200, 300)) # 定義好切片規(guī)則,到處使用 first_half = slice(None, 50) # 等價(jià)于 [:50] last_quarter = slice(-25, None) # 等價(jià)于 [-25:] every_10th = slice(None, None, 10) # 等價(jià)于 [::10] print(data1[first_half]) print(data2[last_quarter]) print(data3[every_10th])
7.2 切片對(duì)象的屬性
s = slice(2, 10, 3)
print(s.start) # 2
print(s.stop) # 10
print(s.step) # 3
# indices()方法:根據(jù)序列長(zhǎng)度規(guī)范化切片參數(shù)
lst = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
print(s.indices(len(lst))) # (2, 10, 3)
# 這意味著實(shí)際提取的是索引2, 5, 8
# 當(dāng)切片參數(shù)超出范圍時(shí),indices()會(huì)自動(dòng)修正
s2 = slice(5, 100, 2)
print(s2.indices(10)) # (5, 10, 2)(stop從100修正為10)
s3 = slice(-100, 3, 1)
print(s3.indices(10)) # (0, 3, 1)(start從-100修正為0)
# 用indices()實(shí)現(xiàn)自己的安全切片操作
def my_slice(sequence, slice_obj):
"""使用slice對(duì)象的通用安全切片"""
start, stop, step = slice_obj.indices(len(sequence))
result = []
i = start
if step > 0:
while i < stop:
result.append(sequence[i])
i += step
else:
while i > stop:
result.append(sequence[i])
i += step
return type(sequence)(result) if hasattr(type(sequence), '__call__') else result
7.3 切片在自定義類中的應(yīng)用
class DataSeries:
"""自定義的數(shù)據(jù)庫(kù)列——演示如何支持切片操作"""
def __init__(self, data):
self._data = list(data)
def __getitem__(self, key):
"""支持索引和切片"""
if isinstance(key, slice):
# 可以用indices()規(guī)范化切片參數(shù)
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]
else:
raise TypeError(f'不支持的索引類型:{type(key)}')
def __setitem__(self, key, value):
"""支持索引賦值和切片賦值"""
if isinstance(key, slice):
self._data[key] = value
elif isinstance(key, int):
self._data[key] = value
else:
raise TypeError(f'不支持的索引類型:{type(key)}')
def __delitem__(self, key):
"""支持通過(guò)切片刪除"""
if isinstance(key, slice):
del self._data[key]
elif isinstance(key, int):
del self._data[key]
def __len__(self):
return len(self._data)
def __repr__(self):
return f'DataSeries({self._data})'
ds = DataSeries([10, 20, 30, 40, 50, 60, 70, 80, 90, 100])
# 使用切片
print(ds[2:5]) # DataSeries([30, 40, 50])
print(ds[::2]) # DataSeries([10, 30, 50, 70, 90])
print(ds[::-1]) # DataSeries([100, 90, 80, 70, 60, 50, 40, 30, 20, 10])
# 切片賦值
ds[2:5] = [300, 400, 500]
print(ds) # DataSeries([10, 20, 300, 400, 500, 60, 70, 80, 90, 100])
# 切片刪除
del ds[3:7]
print(ds) # DataSeries([10, 20, 300, 70, 80, 90, 100])
八、切片的性能與陷阱
8.1 切片的內(nèi)存消耗
import sys
# 切片總是創(chuàng)建新列表——對(duì)大列表會(huì)導(dǎo)致內(nèi)存翻倍
large_list = list(range(10000000)) # 1000萬(wàn)元素
# 取一小段也會(huì)創(chuàng)建新列表(雖然只包含幾個(gè)元素)
small_slice = large_list[1000:1010]
print(f'原列表大小: {sys.getsizeof(large_list):,} 字節(jié)')
print(f'切片大小: {sys.getsizeof(small_slice):,} 字節(jié)')
# 如果只需要遍歷,不需要切片
# 不推薦(創(chuàng)建新列表)
for item in large_list[1000:1010]:
print(item)
# 推薦(用islice避免創(chuàng)建新列表)
from itertools import islice
for item in islice(large_list, 1000, 1010):
print(item)
8.2 切片的常見(jiàn)陷阱
# 陷阱一:忘記切片是淺拷貝 original = [[0]] * 5 copy = original[:] copy[0][0] = 999 print(original) # [[999], [999], [999], [999], [999]](所有元素都變了?。? print(copy) # [[999], [999], [999], [999], [999]] # 陷阱二:對(duì)切片賦值 vs 對(duì)切片變量賦值 lst = [1, 2, 3, 4, 5] s = lst[1:4] # s是一個(gè)新列表 s[0] = 100 # 修改s不影響lst print(lst) # [1, 2, 3, 4, 5](lst沒(méi)變) lst[1:4] = [100, 100, 100] # 這是切片賦值,修改lst本身 print(lst) # [1, 100, 100, 100, 5] # 陷阱三:帶步長(zhǎng)的切片賦值長(zhǎng)度必須匹配 lst = [0, 1, 2, 3, 4, 5] # lst[::2] = [10, 20] # ValueError(3個(gè)位置但只給了2個(gè)值) # 陷阱四:負(fù)數(shù)步長(zhǎng)時(shí)start應(yīng)大于stop lst = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] print(lst[2:8:2]) # [2, 4, 6](正確) print(lst[8:2:-2]) # [8, 6, 4](正確) print(lst[2:8:-2]) # [](空!start < stop,反向步長(zhǎng)取不到值) # 陷阱五:給切片賦值的右邊不一定是列表 lst = [1, 2, 3, 4, 5] lst[1:4] = 'XYZ' # 字符串被拆為字符序列 print(lst) # [1, 'X', 'Y', 'Z', 5] lst[1:4] = range(100, 103) # range也是可迭代對(duì)象 print(lst) # [1, 100, 101, 102, 5]
九、綜合實(shí)戰(zhàn):用切片優(yōu)雅解決實(shí)際問(wèn)題
9.1 回文判斷
def is_palindrome(s):
"""用切片判斷回文"""
# 去掉非字母數(shù)字字符,統(tǒng)一小寫
cleaned = ''.join(c.lower() for c in s if c.isalnum())
return cleaned == cleaned[::-1]
print(is_palindrome('A man, a plan, a canal: Panama')) # True
print(is_palindrome('race a car')) # False
print(is_palindrome('上海自來(lái)水來(lái)自海上')) # True
9.2 楊輝三角
def pascal_triangle(n):
"""生成楊輝三角——用切片輕松實(shí)現(xiàn)"""
result = [[1]]
for _ in range(1, n):
prev = result[-1]
# 核心:每行前后補(bǔ)0,相鄰元素相加
current = [1] + [prev[i] + prev[i + 1] for i in range(len(prev) - 1)] + [1]
result.append(current)
return result
# 更Pythonic的寫法(用切片)
def pascal_triangle_v2(n):
result = [[1]]
for _ in range(1, n):
prev = [0] + result[-1] + [0]
current = [prev[i] + prev[i + 1] for i in range(len(prev) - 1)]
result.append(current)
return result
for row in pascal_triangle_v2(8):
print(' '.join(f'{x:3d}' for x in row).center(40))
9.3 格式化輸出對(duì)齊
def format_columns(data, col_widths):
"""用切片分列展示數(shù)據(jù)"""
result = []
for row in data:
formatted = [str(cell)[:w].ljust(w) for cell, w in zip(row, col_widths)]
result.append(' | '.join(formatted))
return '\n'.join(result)
headers = ['姓名', '年齡', '城市', '職業(yè)']
rows = [
['小明', 25, '北京', '軟件工程師'],
['小紅', 23, '上海', 'UI設(shè)計(jì)師'],
['Alexander Hamilton', 26, '廣州', '數(shù)據(jù)科學(xué)家'],
]
# 計(jì)算列寬
col_widths = []
for i in range(len(headers)):
max_w = max(len(str(row[i])) for row in [headers] + rows)
col_widths.append(max_w + 2)
print(format_columns([headers] + rows, col_widths))
十、本篇小結(jié)
切片是Python中最強(qiáng)大、最常用的序列操作工具:
- 完整語(yǔ)法:
lst[start:stop:step],三個(gè)參數(shù)都可以省略,默認(rèn)值根據(jù)step的正負(fù)確定 - 永不越界:切片訪問(wèn)永遠(yuǎn)不會(huì)報(bào)IndexError,越界自動(dòng)截?cái)?mdash;—這是它和索引訪問(wèn)的關(guān)鍵區(qū)別
- 原地修改:
lst[a:b] = values可以增刪改列表元素,lst[a:a] = values在任意位置插入,lst[a:b] = []刪除一段 - 淺拷貝:
lst[:]創(chuàng)建新列表,但內(nèi)層對(duì)象共享(嵌套列表要注意) - 切片對(duì)象:
slice(start, stop, step)可復(fù)用切片規(guī)則,.indices(len)規(guī)范化參數(shù) - 帶步長(zhǎng)的切片賦值:右邊長(zhǎng)度必須和切片結(jié)果長(zhǎng)度一致
切片操作是Python編程中的"基本功",但用好了就是"殺手锏"。熟練運(yùn)用切片可以讓你的代碼減少一半的循環(huán)和條件判斷。下一篇我們將進(jìn)入列表的增刪改查方法詳解——從增加元素的四大方式開(kāi)始。
到此這篇關(guān)于Python基礎(chǔ)指南之列表切片操作的高級(jí)應(yīng)用場(chǎng)景的文章就介紹到這了,更多相關(guān)Python列表切片內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
python項(xiàng)目--使用Tkinter的日歷GUI應(yīng)用程序
在 Python 中,我們可以使用 Tkinter 制作 GUI。如果你非常有想象力和創(chuàng)造力,你可以用 Tkinter 做出很多有趣的東西,希望本篇文章能夠幫到你2021-08-08
解決Python內(nèi)層for循環(huán)如何break出外層的循環(huán)的問(wèn)題
今天小編就為大家分享一篇解決Python內(nèi)層for循環(huán)如何break出外層的循環(huán)的問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2019-06-06
OpenCV實(shí)戰(zhàn)記錄之基于分水嶺算法的圖像分割
在機(jī)器視覺(jué)中,有時(shí)需要對(duì)產(chǎn)品進(jìn)行檢測(cè)和計(jì)數(shù),其難點(diǎn)無(wú)非是對(duì)于產(chǎn)品的圖像分割,這篇文章主要給大家介紹了關(guān)于OpenCV實(shí)戰(zhàn)記錄之基于分水嶺算法的圖像分割的相關(guān)資料,需要的朋友可以參考下2023-02-02
python實(shí)現(xiàn)布隆過(guò)濾器及原理解析
布隆過(guò)濾器( BloomFilter )是一種數(shù)據(jù)結(jié)構(gòu),比較巧妙的概率型數(shù)據(jù)結(jié)構(gòu)(probabilistic data structure),特點(diǎn)是高效地插入和查詢,可以用來(lái)告訴你 “某樣?xùn)|西一定不存在或者可能存在”。這篇文章主要介紹了python實(shí)現(xiàn)布隆過(guò)濾器 ,需要的朋友可以參考下2019-12-12
Python?matplotlib?seaborn繪圖教程詳解
Seaborn是在matplotlib的基礎(chǔ)上進(jìn)行了更高級(jí)的API封裝,從而使得作圖更加容易,在大多數(shù)情況下使用seaborn就能做出很具有吸引力的圖。本文將詳細(xì)講解如何利用Seaborn繪制圖表,需要的可以參考一下2022-03-03
Django ORM filter() 的運(yùn)用詳解
這篇文章主要介紹了Django ORM filter() 的運(yùn)用詳解,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2020-05-05

