式實戰(zhàn)指南)
從入門到實戰(zhàn)聊聊Python列表List那些高頻方法Python的列表List是我日常寫腳本、做數(shù)據(jù)處理時用得最多的內(nèi)置數(shù)據(jù)結(jié)構(gòu)沒有之一。不管你是剛開始學(xué)Python還是已經(jīng)寫了一段時間但總感覺對列表的掌握還差點意思這篇內(nèi)容應(yīng)該都能幫到你。我盡量不繞彎子直接講清楚列表常用方法的用法、底層邏輯和最容易踩的坑同時結(jié)合平時處理數(shù)據(jù)、寫爬蟲、做量化策略時的真實場景來展開保證你看完能直接用起來。列表這個數(shù)據(jù)結(jié)構(gòu)說白了就是一個有序的、可以隨時增刪改查的容器元素類型可以混著放比如整數(shù)、字符串、甚至嵌套另一個列表。這種靈活性讓它成了Python里最“萬能”的存儲方式之一但也正因為太靈活很多人在使用方法時容易忽略細節(jié)比如append和extend的區(qū)別、切片復(fù)制和copy()的區(qū)別、sort和sorted的返回值陷阱這些坑我早年都踩過后面會一一拆開講。這篇文章適合的讀者很明確剛學(xué)完基礎(chǔ)語法想進階的人、工作中需要頻繁操作列表但經(jīng)常查文檔的人、以及準備面試想系統(tǒng)梳理列表常用方法的人。我會按“創(chuàng)建 - 增刪改查 - 排序復(fù)制 - 推導(dǎo)式 - 實戰(zhàn)案例 - 常見問題排查”這條線來講每個方法都有示例代碼和說明屬于那種可以一邊看一邊抄作業(yè)的教程。1. 列表基礎(chǔ)操作創(chuàng)建、索引與切片1.1 創(chuàng)建列表的幾種方式和常見誤區(qū)Python創(chuàng)建列表的方式很直白最常見的就是用方括號# 創(chuàng)建空列表 empty_list [] # 創(chuàng)建帶初始值的列表 fruits [apple, banana, cherry] # 用 list() 構(gòu)造函數(shù) numbers list((1, 2, 3)) # 字符串轉(zhuǎn)列表 chars list(hello) print(chars) # [h, e, l, l, o]這里有一個很容易被忽略的點list(hello)會把字符串拆成一個個字符。如果你想把字符串按單詞切分應(yīng)該用split()而不是list()。很多剛開始寫代碼的朋友在這里栽過跟頭拿著list(hello world)想要[hello, world]結(jié)果得到了一堆單獨字符然后一臉懵。還有一個創(chuàng)建列表的經(jīng)典坑就是用乘法復(fù)制嵌套列表# 看起來像是在創(chuàng)建二維列表 matrix [[0] * 3] * 3 print(matrix) # [[0, 0, 0], [0, 0, 0], [0, 0, 0]] # 修改一個元素 matrix[0][0] 1 print(matrix) # [[1, 0, 0], [1, 0, 0], [1, 0, 0]]看到了嗎我只改了一行結(jié)果三行全變了。原因在于[[0] * 3] * 3先把[0, 0, 0]這個列表創(chuàng)建了一份然后乘3次只是復(fù)制了“引用”也就是說三個子列表其實指向的是同一個內(nèi)存對象。正確的做法是用列表推導(dǎo)式matrix [[0] * 3 for _ in range(3)]這里也順帶引出一個基礎(chǔ)概念Python變量名存的是對象的引用而不是對象本身。理解了這一點后面講淺拷貝、深拷貝就會輕松很多。1.2 索引和切片不只是簡單的取元素列表的索引從0開始支持正數(shù)索引和負數(shù)索引。負數(shù)索引從-1開始對應(yīng)最后一個元素。這一點很好記list[-1]永遠是最后一個元素list[-2]是倒數(shù)第二個。nums [10, 20, 30, 40, 50] print(nums[0]) # 10 print(nums[-1]) # 50 print(nums[-3]) # 30切片是列表操作里最靈活、也最有意思的部分語法是list[start:stop:step]。注意切片是“左閉右開”也就是包含start對應(yīng)的元素但不包含stop對應(yīng)的元素。這一點必須刻在腦子里不然寫切片時總是差一個元素。nums [10, 20, 30, 40, 50] # 取前三個 print(nums[:3]) # [10, 20, 30] # 取后兩個 print(nums[-2:]) # [40, 50] # 隔一個取一個 print(nums[::2]) # [10, 30, 50] # 反轉(zhuǎn)列表的切片寫法 print(nums[::-1]) # [50, 40, 30, 20, 10]切片還有一個隱藏能力可以賦值用來替換或插入元素。nums [10, 20, 30, 40, 50] nums[1:3] [99, 98, 97] print(nums) # [10, 99, 98, 97, 40, 50]這里要補充一下切片返回的是一個新的列表淺拷貝所以用切片復(fù)制列表在某種程度上是安全的。但它只是淺拷貝如果列表內(nèi)部還有嵌套列表那嵌套列表依然共享引用這一點后面在講到復(fù)制陷阱時還會再強調(diào)。2. 列表的增刪改查高頻方法逐個拆解2.1 增加元素append、extend、insert的區(qū)別列表增加元素的方法有append、extend、insert三個很多人搞不清前兩個的區(qū)別其實一句話就能說透append把參數(shù)當(dāng)作“一個整體”添加到末尾extend把參數(shù)“拆開”逐個添加到末尾。a [1, 2, 3] a.append([4, 5]) print(a) # [1, 2, 3, [4, 5]] b [1, 2, 3] b.extend([4, 5]) print(b) # [1, 2, 3, 4, 5]append可以添加任何類型的對象包括列表、字典、元組。extend通常用于添加可迭代對象比如列表、元組、字符串、集合。如果extend一個字符串它會按字符拆分c [1, 2] c.extend(ab) print(c) # [1, 2, a, b]insert有兩個參數(shù)第一個是插入位置第二個是插入元素d [1, 2, 3] d.insert(1, x) print(d) # [1, x, 2, 3]我的實操經(jīng)驗是insert在列表頭部插入元素時效率比較低因為涉及到所有元素的移位。如果需要頻繁從頭部插入優(yōu)先考慮collections.deque它的appendleft是 O(1) 的。這個細節(jié)在數(shù)據(jù)量小的時候無所謂但處理幾萬條以上數(shù)據(jù)時就會明顯感覺到差別。2.2 刪除元素remove、pop、del、clear各有用處remove按值刪除第一個匹配的元素如果值不存在會拋出ValueErrornums [1, 2, 3, 2, 4] nums.remove(2) print(nums) # [1, 3, 2, 4]注意remove只刪除第一個匹配的2后面的2還留著。如果要刪除所有匹配元素可以用循環(huán)或列表推導(dǎo)式。pop按索引刪除并返回被刪除的元素如果不傳索引默認刪除最后一個nums [1, 2, 3] last nums.pop() print(last) # 3 print(nums) # [1, 2] first nums.pop(0) print(first) # 1pop(0)和remove一樣時間復(fù)雜度是 O(n)因為刪除后所有后續(xù)元素都要往前移動。如果頻繁刪除頭部元素同樣建議用deque。del是Python的通用刪除語句可以按索引刪除、按切片刪除甚至直接刪除整個列表nums [1, 2, 3, 4, 5] del nums[0] print(nums) # [2, 3, 4, 5] del nums[1:3] print(nums) # [2, 5] del nums # 整個列表被刪除clear方法更簡單直接清空列表所有元素但列表對象本身還在nums [1, 2, 3] nums.clear() print(nums) # []2.3 查找與計數(shù)index、count、in 的組合用法index返回元素第一次出現(xiàn)的索引如果元素不存在會拋ValueError。所以用的時候最好先判斷或者捕獲異常這一點實際寫代碼時很關(guān)鍵nums [10, 20, 30, 20, 40] nums.index(20) # 1 nums.index(20, 2) # 從索引2開始找結(jié)果是3 # 安全寫法 if 20 in nums: print(nums.index(20))count統(tǒng)計元素出現(xiàn)的次數(shù)nums [1, 2, 2, 3, 2] print(nums.count(2)) # 3in和not in用來判斷元素是否存在。很多人會忽略的一點是列表的in判斷是線性掃描時間復(fù)雜度 O(n)。如果數(shù)據(jù)量很大且需要頻繁做存在性判斷最好把列表轉(zhuǎn)成集合再判斷集合的in是 O(1)。我處理去重類需求時經(jīng)常這么做效果立竿見影。3. 排序與反轉(zhuǎn)sort、sorted、reverse的細節(jié)和陷阱3.1 sort方法和sorted函數(shù)到底怎么選這是我在面試別人時特別喜歡問的一個點也是實際工作中很容易用混的地方。簡單來說list.sort()是列表的方法原地排序不返回新列表返回None。sorted(iterable)是內(nèi)置函數(shù)排序后返回新列表原列表不變。nums [3, 1, 2] nums.sort() print(nums) # [1, 2, 3]原列表被修改nums [3, 1, 2] new_nums sorted(nums) print(nums) # [3, 1, 2]原列表不變 print(new_nums) # [1, 2, 3]返回新列表很多新手在這里寫了nums nums.sort()然后發(fā)現(xiàn)nums變成了None這就是沒記住sort是原地操作、返回None導(dǎo)致的。反過來如果寫nums sorted(nums)那就沒問題因為sorted返回了新列表。兩個方法都支持key參數(shù)用來指定排序依據(jù)這是最常用的高級用法words [banana, apple, cherry, date] words.sort(keylen) print(words) # [date, apple, banana, cherry]按長度排序key還可以配合lambda、函數(shù)、甚至內(nèi)置方法比如按字符串最后一個字母排序words [banana, apple, cherry] words.sort(keylambda s: s[-1]) print(words) # [banana, apple, cherry]3.2 復(fù)雜對象排序用key參數(shù)實現(xiàn)按字典、按元組元素排序key參數(shù)最大的價值在于能對復(fù)雜數(shù)據(jù)排序。假設(shè)你有一個包含字典的列表每個字典代表一個商品包含名稱和價格想按價格從低到高排序products [ {name: apple, price: 5}, {name: banana, price: 3}, {name: cherry, price: 8}, ] products.sort(keylambda p: p[price]) print(products) # [{name: banana, price: 3}, {name: apple, price: 5}, {name: cherry, price: 8}]這個寫法的效率很高因為key函數(shù)只會對每個元素調(diào)用一次然后Python基于這些 key 值做排序。類似地如果你有一個元組列表想按元組的第二個元素排序pairs [(b, 2), (a, 3), (c, 1)] pairs.sort(keylambda x: x[1]) print(pairs) # [(c, 1), (b, 2), (a, 3)]還有一種更取巧的寫法利用operator.itemgetter性能和可讀性都不錯from operator import itemgetter pairs.sort(keyitemgetter(1))itemgetter比lambda快一些尤其在數(shù)據(jù)量大的時候更明顯。它還可以接收多個參數(shù)實現(xiàn)先按第一個字段排、再按第二個字段排的效果。3.3 reverse和反轉(zhuǎn)的兩種方式reverse()是原地反轉(zhuǎn)返回None注意和[::-1]的區(qū)別[::-1]是生成一個新列表原列表不變。nums [1, 2, 3] nums.reverse() print(nums) # [3, 2, 1]配合sort反向排序可以直接用reverseTrue參數(shù)而不是排序后再reverse效率上更優(yōu)nums [3, 1, 2] nums.sort(reverseTrue) print(nums) # [3, 2, 1]4. 列表推導(dǎo)式與遍歷技巧少寫循環(huán)的寫法4.1 列表推導(dǎo)式的基本語法和進階寫法列表推導(dǎo)式是Python里我最喜歡的一個特性沒有之一。它用一行代碼代替多行循環(huán)既能生成新列表也能做篩選和變換。基本語法是[表達式 for 變量 in 可迭代對象 if 條件]。最簡單的例子生成1到10的平方squares [x ** 2 for x in range(1, 11)] print(squares) # [1, 4, 9, 16, 25, 36, 49, 64, 81, 100]篩選偶數(shù)evens [x for x in range(1, 11) if x % 2 0] print(evens) # [2, 4, 6, 8, 10]嵌套循環(huán)也可以用推導(dǎo)式實現(xiàn)。比如生成一個坐標(biāo)點列表points [(x, y) for x in range(3) for y in range(3)] print(points) # [(0, 0), (0, 1), (0, 2), (1, 0), (1, 1), (1, 2), (2, 0), (2, 1), (2, 2)]一般經(jīng)驗是如果推導(dǎo)式嵌套超過兩層可讀性會明顯下降這時候老老實實寫循環(huán)反而更好維護。技術(shù)是為了做事不是為了炫技。4.2 列表推導(dǎo)式與map、filter的對比Python內(nèi)置了map和filter它們也可以用來對列表做變換和篩選但在大部分場景下列表推導(dǎo)式是更直觀的選擇。對比例子nums [1, 2, 3, 4] # 用 map squares_map list(map(lambda x: x ** 2, nums)) # 用列表推導(dǎo)式 squares_comp [x ** 2 for x in nums]map生成的是迭代器對象所以要用list()轉(zhuǎn)成列表。map的優(yōu)點是延遲計算處理超大集合時節(jié)省內(nèi)存但如果你只是構(gòu)建一個小列表直接用推導(dǎo)式更清晰。filter的用法和map類似nums [1, 2, 3, 4] # 用 filter evens_filter list(filter(lambda x: x % 2 0, nums)) # 用列表推導(dǎo)式 evens_comp [x for x in nums if x % 2 0]結(jié)論是map和filter在配合函數(shù)式編程風(fēng)格時有用但日常開發(fā)用列表推導(dǎo)式就夠了。我個人的習(xí)慣是除非性能測試明確說明map和filter有優(yōu)勢否則一律用推導(dǎo)式代碼的可讀性決定了后期的維護成本。4.3 enumerate和zip遍歷時同時拿索引和多個列表enumerate是我遍歷列表時最常用的函數(shù)它可以同時返回索引和元素避免手動維護一個計數(shù)變量fruits [apple, banana, cherry] for index, fruit in enumerate(fruits): print(index, fruit) # 0 apple # 1 banana # 2 cherryenumerate還支持第二個參數(shù)定義起始索引for index, fruit in enumerate(fruits, start1): print(index, fruit) # 1 apple # 2 banana # 3 cherryzip用于同時遍歷多個列表把對應(yīng)位置的元素打包成元組names [Alice, Bob, Charlie] scores [85, 92, 78] for name, score in zip(names, scores): print(f{name}: {score}) # Alice: 85 # Bob: 92 # Charlie: 78zip在數(shù)據(jù)對齊時非常高效。注意如果兩個列表長度不一致zip默認以短的為準。如果想以長的為準并用默認值填充需要用到itertools.zip_longest這個在數(shù)據(jù)分析場景中偶爾會用到。4.4 遍歷時能不能修改列表這是經(jīng)典陷阱在遍歷列表的同時刪除元素會導(dǎo)致索引錯位漏掉一些元素。nums [1, 2, 3, 4, 5] for num in nums: if num % 2 0: nums.remove(num) print(nums) # [1, 3, 5]看似結(jié)果正確但如果換個例子就會出問題nums [2, 4, 5, 6] for num in nums: if num % 2 0: nums.remove(num) print(nums) # [4, 5]為什么會漏掉4因為刪除2后4往前移了一位循環(huán)內(nèi)部索引自動加1導(dǎo)致4被跳過了。這是面試題里非常經(jīng)典的一個坑。正確的做法是遍歷列表的副本或者用列表推導(dǎo)式直接生成新列表nums [2, 4, 5, 6] nums [num for num in nums if num % 2 ! 0] print(nums) # [5]還有一個笨但可靠的方法是倒序遍歷nums [2, 4, 5, 6] for i in range(len(nums) - 1, -1, -1): if nums[i] % 2 0: nums.pop(i) print(nums) # [5]倒序刪除不會影響前面元素的索引所以不會漏元素。我個人最推薦列表推導(dǎo)式因為它簡潔、快、不易出錯。5. 復(fù)制與淺拷貝深拷貝copy方法背后的內(nèi)存邏輯5.1 直接賦值、切片復(fù)制、copy() 的區(qū)別在Python里list2 list1并不是復(fù)制列表而是讓list2和list1指向同一個列表對象。修改任何一個另一個也會變a [1, 2, 3] b a b.append(4) print(a) # [1, 2, 3, 4] a也變了如果你只想復(fù)制內(nèi)容而不是共享引用有三種常見方式a [1, 2, 3] # 方式1切片 b a[:] # 方式2copy方法 c a.copy() # 方式3list構(gòu)造函數(shù) d list(a) b.append(4) print(a) # [1, 2, 3] a不變 print(b) # [1, 2, 3, 4]這三種都是淺拷貝對于一層列表來說夠用了。但列表里如果還有嵌套列表淺拷貝只會復(fù)制外層列表內(nèi)層列表還是共享同一個對象。a [[1, 2], [3, 4]] b a.copy() b[0][0] 99 print(a) # [[99, 2], [3, 4]] a的內(nèi)部列表也被改了5.2 什么時候必須用copy模塊的deepcopy如果列表是多層嵌套且需要完全獨立復(fù)制必須用copy.deepcopy()import copy a [[1, 2], [3, 4]] b copy.deepcopy(a) b[0][0] 99 print(a) # [[1, 2], [3, 4]] a完全不受影響 print(b) # [[99, 2], [3, 4]]deepcopy會遞歸復(fù)制所有層級代價是速度慢、內(nèi)存占用高。所以在使用時想清楚如果列表只有一層用copy()就夠了沒必要用deepcopy。如果列表嵌套層級很深但不需要完全獨立比如你只讀不改也別用deepcopy那是性能殺手。6. 實戰(zhàn)案例從去重、詞頻統(tǒng)計到復(fù)雜數(shù)據(jù)排序6.1 列表去重的幾種寫法哪種最高效列表去重是面試高頻題也是日常開發(fā)常遇到的需求。最簡單的寫法是用集合nums [1, 2, 2, 3, 3, 3] unique list(set(nums)) print(unique) # [1, 2, 3]但這種方法會改變元素順序集合是無序的。如果需要保留原始順序可以用循環(huán)配合輔助集合nums [3, 1, 2, 3, 2, 1] seen set() unique [] for num in nums: if num not in seen: seen.add(num) unique.append(num) print(unique) # [3, 1, 2]Python 3.7 中dict保持插入順序所以還有一種更簡潔的寫法nums [3, 1, 2, 3, 2, 1] unique list(dict.fromkeys(nums)) print(unique) # [3, 1, 2]如果列表元素是字典等不可哈希對象集合方式就不能用了需要自己寫判斷邏輯。實戰(zhàn)中我用dict.fromkeys比較多因為它保留了順序而且性能很好。6.2 詞頻統(tǒng)計列表、字典和collections.Counter詞頻統(tǒng)計是文本處理中的經(jīng)典場景。先看基礎(chǔ)版用字典手動統(tǒng)計words [apple, banana, apple, orange, banana, apple] word_count {} for word in words: word_count[word] word_count.get(word, 0) 1 print(word_count) # {apple: 3, banana: 2, orange: 1}dict.get(word, 0)的寫法比if word in word_count更簡潔也是Python社區(qū)常見的風(fēng)格。不過更推薦直接用collections.Counter它本身就是專為計數(shù)設(shè)計的from collections import Counter words [apple, banana, apple, orange, banana, apple] word_count Counter(words) print(word_count) # Counter({apple: 3, banana: 2, orange: 1}) # 取出現(xiàn)次數(shù)最多的前2個 print(word_count.most_common(2)) # [(apple, 3), (banana, 2)]Counter還有update、subtract、elements等方法功能很全。如果你要統(tǒng)計的數(shù)據(jù)量很大寫成Counter(words)一行就能搞定比手寫循環(huán)省事很多。6.3 按優(yōu)先級排序多個字段實際開發(fā)里經(jīng)常遇到按多個字段排序的需求。比如一個學(xué)生列表每個學(xué)生有語文、數(shù)學(xué)、英語三門成績想按總分降序總分相同按數(shù)學(xué)成績降序排序students [ {name: Alice, chinese: 90, math: 85, english: 78}, {name: Bob, chinese: 85, math: 90, english: 88}, {name: Charlie, chinese: 90, math: 92, english: 70}, ] # 先按數(shù)學(xué)成績降序再按總分降序 students.sort( keylambda s: (s[chinese] s[math] s[english], s[math]), reverseTrue )這里有個容易出錯的地方如果多個排序字段的方向不一致比如總分升序但數(shù)學(xué)降序直接傳reverseTrue會讓所有字段都反向。這種情況下有兩種解決辦法一是把數(shù)字取負二是用functools.cmp_to_key寫自定義比較函數(shù)。# 總分升序數(shù)學(xué)降序 students.sort(keylambda s: (s[chinese] s[math] s[english], -s[math]))用負數(shù)取反是很Pythonic的寫法前提是字段是數(shù)字。如果是字符串字段就需要用更復(fù)雜的方案了。我處理這類需求時優(yōu)先把排序邏輯拆清楚避免在一個lambda里堆太多邏輯那樣后面維護時會很痛苦。6.4 列表分組與結(jié)構(gòu)化處理把一個大列表按某種規(guī)則分組也是高頻需求。比如有一批訂單記錄想按商品名稱分組把相同商品的訂單匯總到一個列表里orders [ {product: apple, amount: 5}, {product: banana, amount: 3}, {product: apple, amount: 8}, {product: cherry, amount: 2}, ] grouped {} for order in orders: product order[product] grouped.setdefault(product, []).append(order) # 或者用 defaultdict from collections import defaultdict grouped defaultdict(list) for order in orders: grouped[order[product]].append(order) for product, items in grouped.items(): total sum(item[amount] for item in items) print(product, total) # apple 13 # banana 3 # cherry 2setdefault和defaultdict都需要掌握。defaultdict(list)的寫法在遍歷時更自然不會因為鍵不存在而報錯。如果你需要的是按條件把列表拆成多個子列表比如把大于10和小于等于10的分開列表推導(dǎo)式可以做但多次遍歷效率不高。數(shù)據(jù)量不大時無所謂數(shù)據(jù)量大時可以考慮用單次遍歷分組nums [3, 15, 7, 22, 9, 30] big, small [], [] for num in nums: if num 10: big.append(num) else: small.append(num)7. 常見問題排查遇到這些情況應(yīng)該怎么排查和解決7.1 修改了列表但其他變量也變了如果你用b a這種方式復(fù)制列表修改b時a也會變。這不是Bug是Python變量引用模型導(dǎo)致的。解決方法是使用a.copy()或a[:]。我發(fā)現(xiàn)很多人第一次遇到這個現(xiàn)象時特別困惑一句話解釋就是變量名是標(biāo)簽不是盒子。b a的意思是給同一個列表對象貼了第二張標(biāo)簽不是創(chuàng)建了一個新盒子。7.2 列表刪除元素時漏刪前面已經(jīng)講過遍歷時用remove會漏元素。解決方案是遍歷副本、用推導(dǎo)式、倒序遍歷。這是一個非常高頻的排查點面試中考這個問題的頻率也特別高。如果你在???、LeetCode上刷題遇到“刪除列表中的指定元素”這類題目一定要記得這個坑。7.3 檢查元素是否存在的性能問題當(dāng)列表很大且需要頻繁判斷元素是否在列表中時用in是O(n)的會導(dǎo)致程序明顯變慢。解決辦法是把列表轉(zhuǎn)成set把判斷變成O(1)。但前提是元素是可哈希的。這個優(yōu)化在爬蟲去重場景里我?guī)缀跆焯煊帽热缇S護一個已處理URL的集合如果URL很多用列表存和用集合存性能差距是數(shù)量級的。7.4 列表排序后數(shù)據(jù)錯亂或順序不是預(yù)期排序結(jié)果和自己設(shè)想的不一樣通常是沒有理解sort是原地修改或者在key函數(shù)里寫了錯誤的邏輯。排查方式很簡單先用小數(shù)據(jù)列表打印每一步結(jié)果確認key函數(shù)返回的值是否符合預(yù)期。在lambda里加打印是一種直接粗暴但有效的排查方式。7.5 列表索引越界訪問不存在的索引會報IndexError。如果你不確定索引是否有效可以先判斷長度或者用try...except捕獲異常。另外pop()在空列表上調(diào)用也會報IndexError所以在刪除前要確認列表不為空。7.6 列表、元組、集合、字典怎么選最后補充一個高頻問題list和tuple、set、dict到底應(yīng)該怎么選。簡單區(qū)分標(biāo)準需要有序、可修改、允許重復(fù)用list需要有序、只讀、可哈希用tuple需要去重、無序、快速判斷存在用set需要鍵值映射、快速查找用dict這個選型標(biāo)準在絕大多數(shù)場景下都夠用。我處理量化交易策略數(shù)據(jù)時日線、分鐘線數(shù)據(jù)用list或DataFrame去重和集合判斷用set指標(biāo)映射用dict。每種數(shù)據(jù)結(jié)構(gòu)都有它的定位混著用反而容易出問題。我個人在實際操作中的一個體會是列表方法本身并不復(fù)雜難點在于理解Python的引用模型、可變對象的機制以及不同方法在不同場景下的性能差異。把這些底層邏輯搞清楚了列表用法基本就融會貫通了。還有一個小技巧想分享給大家在寫代碼之前先想清楚我是要“原地修改”還是“生成新列表”這兩個方向決定了應(yīng)該用哪一類方法也能幫你避免很多莫名的Bug。希望這篇內(nèi)容能讓你對Python列表有一個系統(tǒng)且扎實的理解日常寫代碼時少踩點坑。