戰(zhàn):從哈希表到高頻應(yīng)用場(chǎng)景解析)
1. 項(xiàng)目概述從“頭歌”實(shí)訓(xùn)看Python字典的實(shí)戰(zhàn)價(jià)值最近在輔導(dǎo)一些同學(xué)完成湖南大學(xué)“頭歌”平臺(tái)的Python實(shí)訓(xùn)作業(yè)發(fā)現(xiàn)“實(shí)驗(yàn)9字典”這個(gè)關(guān)卡成了不少新手從理解語(yǔ)法到實(shí)際應(yīng)用的一道分水嶺。很多人學(xué)Python列表、元組還能靠死記硬背一到字典這里面對(duì)“鍵值對(duì)”、“映射”、“哈希表”這些概念就開(kāi)始犯暈寫(xiě)出來(lái)的代碼要么是KeyError滿(mǎn)天飛要么就是效率低下用列表硬生生模擬字典的功能。其實(shí)字典dict是Python里最強(qiáng)大、最高效的數(shù)據(jù)結(jié)構(gòu)之一絕不僅僅是課本上的一個(gè)知識(shí)點(diǎn)。無(wú)論是你未來(lái)想做數(shù)據(jù)分析比如用字典統(tǒng)計(jì)詞頻、Web開(kāi)發(fā)處理JSON數(shù)據(jù)本質(zhì)上就是在操作字典還是寫(xiě)點(diǎn)小工具自動(dòng)化日常任務(wù)比如管理配置文件字典都是你繞不開(kāi)的核心工具。這次我就結(jié)合“頭歌”實(shí)訓(xùn)中常見(jiàn)的題型和實(shí)際開(kāi)發(fā)中的高頻場(chǎng)景把字典那點(diǎn)事徹底講透讓你不僅能輕松通關(guān)實(shí)驗(yàn)更能真正掌握這把利器。2. 字典核心概念與底層邏輯拆解2.1 為什么需要字典從現(xiàn)實(shí)場(chǎng)景到數(shù)據(jù)結(jié)構(gòu)在講語(yǔ)法之前我們先想一個(gè)場(chǎng)景你有一本通訊錄想快速找到“張三”的電話(huà)號(hào)碼。你不會(huì)從第一頁(yè)開(kāi)始逐行掃描而是直接翻到“Z”開(kāi)頭的部分迅速定位到“張三”。這個(gè)“名字”和“電話(huà)號(hào)碼”的對(duì)應(yīng)關(guān)系以及通過(guò)名字快速查找的機(jī)制就是字典思想的核心。在編程中我們經(jīng)常需要處理這種映射關(guān)系。比如學(xué)生信息學(xué)號(hào) - 姓名、成績(jī)、班級(jí)。商品庫(kù)存商品ID - 商品名稱(chēng)、價(jià)格、庫(kù)存數(shù)量。單詞統(tǒng)計(jì)單詞 - 出現(xiàn)的次數(shù)。配置文件配置項(xiàng)名稱(chēng) - 配置值。如果用列表來(lái)實(shí)現(xiàn)你可能需要維護(hù)兩個(gè)平行的列表一個(gè)放鍵如學(xué)號(hào)一個(gè)放值如學(xué)生信息查找時(shí)需要遍歷鍵列表找到索引后再去值列表取對(duì)應(yīng)數(shù)據(jù)時(shí)間復(fù)雜度是O(n)效率很低。字典通過(guò)哈希表Hash Table實(shí)現(xiàn)理想情況下查找、插入、刪除的平均時(shí)間復(fù)雜度都是O(1)也就是幾乎瞬間完成這與列表的遍歷查找有數(shù)量級(jí)的效率差異。2.2 鍵值對(duì)、哈希與可變性理解字典的三大基石1. 鍵值對(duì)Key-Value Pair 這是字典的基本單位。一個(gè)字典由一系列鍵值對(duì)組成形式為{key1: value1, key2: value2, ...}。key是索引value是數(shù)據(jù)。通過(guò)key可以直接訪(fǎng)問(wèn)、修改其對(duì)應(yīng)的value。2. 鍵Key的特性與哈希 這是字典最關(guān)鍵也最容易出錯(cuò)的地方。字典的鍵必須是可哈希hashable且唯一的??晒R馕吨搶?duì)象在其生命周期內(nèi)必須有一個(gè)固定不變的哈希值可通過(guò)hash()函數(shù)獲取并且能與其他對(duì)象比較通過(guò)__eq__()方法。Python中不可變類(lèi)型通常是可哈希的如整數(shù)、浮點(diǎn)數(shù)、字符串、元組但元組內(nèi)必須全部是可哈希對(duì)象。可變類(lèi)型如列表、字典、集合是不可哈希的因此不能作為字典的鍵。唯一性字典中同一個(gè)鍵只能出現(xiàn)一次。如果賦值時(shí)鍵已存在則會(huì)更新該鍵對(duì)應(yīng)的值。# 合法的鍵 valid_dict { 123: ‘整數(shù)鍵‘, # 整數(shù)可哈希 ‘name‘: ‘字符串鍵‘, # 字符串可哈希 (1, 2): ‘元組鍵‘ # 元組內(nèi)容可哈??晒?} # 非法的鍵 try: invalid_dict {[1, 2]: ‘列表鍵‘} # 列表不可哈希會(huì)引發(fā)TypeError except TypeError as e: print(f“錯(cuò)誤{e}“) # 輸出unhashable type: ‘list‘3. 值Value的靈活性 與鍵相反字典的值可以是任意類(lèi)型的Python對(duì)象包括數(shù)字、字符串、列表、另一個(gè)字典甚至函數(shù)或類(lèi)實(shí)例。一個(gè)字典里可以同時(shí)存放各種不同類(lèi)型的值。flexible_dict { ‘name‘: ‘Alice‘, # 字符串 ‘a(chǎn)ge‘: 25, # 整數(shù) ‘scores‘: [85, 92, 78], # 列表 ‘contact‘: {‘email‘: ‘a(chǎn)example.com‘, ‘phone‘: ‘123456‘}, # 嵌套字典 ‘is_active‘: True # 布爾值 }注意雖然值可以是任何類(lèi)型但為了代碼的可讀性和可維護(hù)性通常建議在一個(gè)字典中相同含義的字段比如多個(gè)人的‘a(chǎn)ge‘使用相同的數(shù)據(jù)類(lèi)型。3. 字典的創(chuàng)建、訪(fǎng)問(wèn)與基本操作全解3.1 四種創(chuàng)建字典的方式及其適用場(chǎng)景花括號(hào){}直接創(chuàng)建最常用student {‘name‘: ‘Bob‘, ‘a(chǎn)ge‘: 20, ‘major‘: ‘CS‘} empty_dict {} # 創(chuàng)建一個(gè)空字典適用場(chǎng)景已知所有鍵值對(duì)直接靜態(tài)定義時(shí)使用。使用dict()構(gòu)造函數(shù)student dict(name‘Bob‘, age20, major‘CS‘) # 關(guān)鍵字參數(shù)鍵會(huì)自動(dòng)轉(zhuǎn)為字符串 student2 dict([(‘name‘, ‘Bob‘), (‘a(chǎn)ge‘, 20)]) # 從可迭代對(duì)象如列表元組創(chuàng)建適用場(chǎng)景當(dāng)鍵是合法的Python標(biāo)識(shí)符字符串且不含特殊字符時(shí)用關(guān)鍵字參數(shù)形式很簡(jiǎn)潔。需要從已有的成對(duì)數(shù)據(jù)如zip函數(shù)的結(jié)果構(gòu)建字典時(shí)。字典推導(dǎo)式強(qiáng)大且高效# 將列表元素映射為其平方 squares {x: x**2 for x in range(5)} # {0: 0, 1: 1, 2: 4, 3: 9, 4: 16} # 過(guò)濾并轉(zhuǎn)換字典 original {‘a(chǎn)‘: 1, ‘b‘: 2, ‘c‘: 3} filtered {k: v*2 for k, v in original.items() if v 1} # {‘b‘: 4, ‘c‘: 6}適用場(chǎng)景需要基于一個(gè)序列或另一個(gè)映射通過(guò)計(jì)算或過(guò)濾來(lái)生成新字典。代碼非常簡(jiǎn)潔執(zhí)行效率也高。fromkeys()方法keys [‘name‘, ‘a(chǎn)ge‘, ‘city‘] default_dict dict.fromkeys(keys, ‘N/A‘) # {‘name‘: ‘N/A‘, ‘a(chǎn)ge‘: ‘N/A‘, ‘city‘: ‘N/A‘}適用場(chǎng)景快速創(chuàng)建一個(gè)新字典為給定的鍵序列提供統(tǒng)一的初始值。常用于初始化配置或模板。3.2 安全地訪(fǎng)問(wèn)與修改字典元素訪(fǎng)問(wèn)字典元素最直接的方式是使用方括號(hào)[]但如果鍵不存在會(huì)引發(fā)KeyError。student {‘name‘: ‘Bob‘, ‘a(chǎn)ge‘: 20} print(student[‘name‘]) # 輸出Bob # print(student[‘grade‘]) # KeyError: ‘grade‘為了避免程序崩潰我們有幾種更安全的訪(fǎng)問(wèn)方式get(key, default)方法首選grade student.get(‘grade‘) # 鍵不存在返回None grade_safe student.get(‘grade‘, ‘未錄入‘) # 鍵不存在返回指定的默認(rèn)值‘未錄入‘ print(grade_safe) # 輸出未錄入這是最推薦的方式特別是在不確定鍵是否存在時(shí)。in成員運(yùn)算符if ‘grade‘ in student: print(student[‘grade‘]) else: print(‘鍵不存在‘)setdefault(key, default)方法 這是一個(gè)“訪(fǎng)問(wèn)兼設(shè)置”的方法。如果鍵存在則返回其值如果鍵不存在則先將key: default插入字典再返回default。# 統(tǒng)計(jì)單詞頻率的經(jīng)典用法 word_counts {} for word in [‘a(chǎn)pple‘, ‘banana‘, ‘a(chǎn)pple‘, ‘orange‘]: word_counts.setdefault(word, 0) # 如果word不在字典中則初始化為0 word_counts[word] 1 # 然后計(jì)數(shù)加1 print(word_counts) # {‘a(chǎn)pple‘: 2, ‘banana‘: 1, ‘orange‘: 1}這比先用in判斷再賦值的寫(xiě)法更簡(jiǎn)潔高效。修改和新增元素的語(yǔ)法是一樣的都是dict[key] value。如果key存在則修改其值如果不存在則新增一個(gè)鍵值對(duì)。student[‘a(chǎn)ge‘] 21 # 修改已存在的鍵‘a(chǎn)ge‘的值 student[‘university‘] ‘湖南大學(xué)‘ # 新增鍵值對(duì)3.3 遍歷字典的三種核心視角遍歷字典時(shí)你需要明確你想獲取什么是鍵、值還是兩者都要。遍歷所有的鍵.keys().keys()方法返回一個(gè)視圖對(duì)象包含字典的所有鍵。直接遍歷字典默認(rèn)就是遍歷鍵。for key in student.keys(): print(key) # 等價(jià)于 for key in student: print(key)遍歷所有的值.values().values()方法返回一個(gè)包含所有值的視圖對(duì)象。for value in student.values(): print(value)遍歷所有的鍵值對(duì).items()這是最常用、最推薦的遍歷方式。.items()返回一個(gè)由(key, value)元組組成的視圖對(duì)象。遍歷時(shí)可以直接解包。for key, value in student.items(): print(f“{key}: {value}“)實(shí)操心得在Python 3中.keys()、.values()、.items()返回的是“視圖對(duì)象”它們不是列表而是動(dòng)態(tài)反映字典當(dāng)前狀態(tài)的“窗口”。這意味著如果你在遍歷過(guò)程中修改了字典的大小增刪鍵可能會(huì)引發(fā)RuntimeError。如果需要固定的快照可以將其轉(zhuǎn)換為列表list(student.items())。4. 字典進(jìn)階操作與內(nèi)置方法實(shí)戰(zhàn)4.1 合并字典的多種策略與選擇在實(shí)際項(xiàng)目中經(jīng)常需要將兩個(gè)或多個(gè)字典合并。Python提供了幾種方式各有優(yōu)劣。更新合并.update() 就地修改原字典將另一個(gè)字典的鍵值對(duì)添加進(jìn)來(lái)。如果有重復(fù)的鍵后者的值會(huì)覆蓋前者。dict1 {‘a(chǎn)‘: 1, ‘b‘: 2} dict2 {‘b‘: 3, ‘c‘: 4} dict1.update(dict2) print(dict1) # {‘a(chǎn)‘: 1, ‘b‘: 3, ‘c‘: 4}特點(diǎn)原地操作修改dict1。適用于明確要以一個(gè)字典為主進(jìn)行更新的場(chǎng)景。解包合并Python 3.5推薦 使用**解包運(yùn)算符可以創(chuàng)建一個(gè)新的合并字典。dict1 {‘a(chǎn)‘: 1, ‘b‘: 2} dict2 {‘b‘: 3, ‘c‘: 4} merged_dict {**dict1, **dict2} print(merged_dict) # {‘a(chǎn)‘: 1, ‘b‘: 3, ‘c‘: 4} print(dict1) # {‘a(chǎn)‘: 1, ‘b‘: 2} 原字典不變特點(diǎn)創(chuàng)建新字典不修改原字典。語(yǔ)法簡(jiǎn)潔直觀(guān)是Python 3.5以后的首選。如果有多個(gè)字典可以連續(xù)解包{**d1, **d2, **d3}。|合并運(yùn)算符Python 3.9 Python 3.9引入了專(zhuān)門(mén)的字典合并運(yùn)算符。dict1 {‘a(chǎn)‘: 1, ‘b‘: 2} dict2 {‘b‘: 3, ‘c‘: 4} merged_dict dict1 | dict2 # 創(chuàng)建新字典 dict1 | dict2 # 原地更新等價(jià)于 dict1.update(dict2)特點(diǎn)語(yǔ)法最簡(jiǎn)潔意圖最明確。如果你的環(huán)境是Python 3.9強(qiáng)烈推薦使用。選擇建議需要保留原字典創(chuàng)建新字典 -解包合并{**d1, **d2}(Py3.5) 或|運(yùn)算符(Py3.9)。需要就地更新原字典 -.update()或|運(yùn)算符(Py3.9)。處理嵌套字典的深度合并上述方法只做淺合并需要自己遞歸實(shí)現(xiàn)或使用collections.ChainMap。4.2 字典排序按鍵還是按值字典本身是無(wú)序的Python 3.6之前是絕對(duì)無(wú)序3.6之后插入順序被保留但官方仍稱(chēng)其為無(wú)序不應(yīng)依賴(lài)順序進(jìn)行編程。如果需要對(duì)字典內(nèi)容進(jìn)行排序輸出通常的做法是按鍵排序my_dict {‘banana‘: 3, ‘a(chǎn)pple‘: 4, ‘pear‘: 1, ‘orange‘: 2} # 返回一個(gè)按鍵排序的鍵值元組列表 sorted_by_key sorted(my_dict.items()) print(sorted_by_key) # [(‘a(chǎn)pple‘, 4), (‘banana‘, 3), (‘orange‘, 2), (‘pear‘, 1)] # 如果需要轉(zhuǎn)回字典Python 3.7保留插入順序 dict_by_key dict(sorted(my_dict.items()))按值排序 使用sorted()函數(shù)的key參數(shù)指定排序的依據(jù)。key參數(shù)應(yīng)是一個(gè)函數(shù)它接收一個(gè)元素這里是(key, value)元組并返回用于比較的值。# 按值升序排序 sorted_by_value sorted(my_dict.items(), keylambda item: item[1]) print(sorted_by_value) # [(‘pear‘, 1), (‘orange‘, 2), (‘banana‘, 3), (‘a(chǎn)pple‘, 4)] # 按值降序排序 sorted_by_value_desc sorted(my_dict.items(), keylambda item: item[1], reverseTrue)lambda item: item[1]是一個(gè)匿名函數(shù)它接收一個(gè)元組item返回其第二個(gè)元素也就是字典的值。注意事項(xiàng)排序操作sorted()返回的是列表而不是字典。如果你需要一個(gè)保持“有序”行為的映射可以考慮使用collections.OrderedDict在Python 3.7后普通dict已有序但OrderedDict在相等性比較等方面仍有特殊行為。4.3 其他重要內(nèi)置方法速查方法描述示例pop(key[, default])移除指定鍵并返回其值。若鍵不存在且未提供default則報(bào)KeyError若提供default則返回default。value d.pop(‘b‘, None)popitem()移除并返回最后插入的Python 3.7或任意一個(gè)Python 3.6及之前鍵值對(duì)形式為(key, value)。字典為空時(shí)報(bào)KeyError。k, v d.popitem()clear()移除字典內(nèi)所有項(xiàng)。d.clear()copy()返回字典的淺拷貝。new_d d.copy()len(d)返回字典中鍵值對(duì)的數(shù)量。count len(d)關(guān)于淺拷貝與深拷貝copy()方法或dict(d)構(gòu)造函數(shù)進(jìn)行的都是淺拷貝。它只復(fù)制字典本身如果字典的值是可變對(duì)象如列表、字典那么拷貝后的字典和原字典會(huì)共享這些可變對(duì)象的引用。修改這些可變對(duì)象會(huì)同時(shí)影響兩個(gè)字典。import copy original {‘a(chǎn)‘: [1, 2, 3]} shallow_copy original.copy() deep_copy copy.deepcopy(original) original[‘a(chǎn)‘].append(4) print(shallow_copy) # {‘a(chǎn)‘: [1, 2, 3, 4]} 被影響了 print(deep_copy) # {‘a(chǎn)‘: [1, 2, 3]} 不受影響當(dāng)字典的值包含嵌套的可變結(jié)構(gòu)時(shí)如果希望完全獨(dú)立必須使用copy.deepcopy()進(jìn)行深拷貝。5. 頭歌實(shí)訓(xùn)典型題型剖析與實(shí)戰(zhàn)代碼“頭歌”平臺(tái)的實(shí)驗(yàn)題目往往注重基礎(chǔ)知識(shí)的靈活運(yùn)用和邊界情況的處理。下面我們針對(duì)“實(shí)驗(yàn)9字典”可能涉及的幾類(lèi)典型題目進(jìn)行思路分析和代碼實(shí)現(xiàn)。5.1 題型一字典的構(gòu)建與基本統(tǒng)計(jì)題目示例從一系列空格分隔的單詞中構(gòu)建一個(gè)字典鍵為單詞值為該單詞出現(xiàn)的次數(shù)。解題思路初始化一個(gè)空字典。分割字符串得到單詞列表。遍歷單詞列表對(duì)每個(gè)單詞使用get()方法或setdefault()方法更新其在字典中的計(jì)數(shù)。輸出字典。參考代碼def word_count(text): “““統(tǒng)計(jì)字符串中單詞頻率“““ words text.split() # 默認(rèn)按任意空白字符分割 count_dict {} for word in words: # 方法1使用get count_dict[word] count_dict.get(word, 0) 1 # 方法2使用setdefault (邏輯稍復(fù)雜但一次操作) # count_dict.setdefault(word, 0) # count_dict[word] 1 return count_dict # 測(cè)試 sample_text “apple banana orange apple banana apple“ result word_count(sample_text) print(result) # {‘a(chǎn)pple‘: 3, ‘banana‘: 2, ‘orange‘: 1}5.2 題型二字典的嵌套與信息查詢(xún)題目示例管理學(xué)生成績(jī)信息。每個(gè)學(xué)生有學(xué)號(hào)、姓名和多門(mén)課程的成績(jī)。實(shí)現(xiàn)添加學(xué)生、根據(jù)學(xué)號(hào)查詢(xún)學(xué)生平均成績(jī)等功能。解題思路使用嵌套字典結(jié)構(gòu)。外層字典的鍵是學(xué)號(hào)值是一個(gè)內(nèi)層字典內(nèi)層字典存儲(chǔ)姓名和各科成績(jī)。添加學(xué)生時(shí)直接賦值或使用update。查詢(xún)時(shí)先判斷學(xué)號(hào)是否存在再計(jì)算平均分。參考代碼# 初始化一個(gè)空的學(xué)生數(shù)據(jù)庫(kù) students {} def add_student(sid, name, scores): “““添加或更新學(xué)生信息“““ students[sid] { ‘name‘: name, ‘scores‘: scores # scores 是一個(gè)字典如 {‘math‘: 90, ‘english‘: 85} } def get_average_score(sid): “““根據(jù)學(xué)號(hào)獲取學(xué)生平均分“““ if sid not in students: return None # 或 raise KeyError student_info students[sid] score_list student_info[‘scores‘].values() average sum(score_list) / len(score_list) return average # 測(cè)試 add_student(‘1001‘, ‘張三‘, {‘math‘: 90, ‘english‘: 85, ‘python‘: 95}) add_student(‘1002‘, ‘李四‘, {‘math‘: 88, ‘english‘: 92}) print(f“學(xué)生數(shù)據(jù)庫(kù){students}“) avg_1001 get_average_score(‘1001‘) print(f“張三的平均分{avg_1001:.2f}“) # 輸出90.005.3 題型三字典與列表的轉(zhuǎn)換及排序題目示例有一個(gè)商品銷(xiāo)售金額的字典需要找出銷(xiāo)售額最高的前N個(gè)商品。解題思路利用sorted()函數(shù)對(duì)字典的項(xiàng)items()進(jìn)行排序key指定按值排序reverseTrue表示降序。使用切片獲取前N項(xiàng)。可以將結(jié)果轉(zhuǎn)換回字典或直接以列表形式輸出。參考代碼def top_n_items(sales_dict, n): “““返回銷(xiāo)售額前N的商品列表形式“““ # 按值降序排序得到商品銷(xiāo)售額元組列表 sorted_items sorted(sales_dict.items(), keylambda item: item[1], reverseTrue) # 取前N個(gè) top_n sorted_items[:n] return top_n def top_n_items_dict(sales_dict, n): “““返回銷(xiāo)售額前N的商品字典形式Python 3.7“““ sorted_items sorted(sales_dict.items(), keylambda item: item[1], reverseTrue) top_n_dict dict(sorted_items[:n]) return top_n_dict # 測(cè)試 sales {‘商品A‘: 15000, ‘商品B‘: 23000, ‘商品C‘: 8000, ‘商品D‘: 45000, ‘商品E‘: 12000} top_3 top_n_items(sales, 3) print(f“銷(xiāo)售額前三列表{top_3}“) # [(‘商品D‘, 45000), (‘商品B‘, 23000), (‘商品A‘, 15000)] top_3_dict top_n_items_dict(sales, 3) print(f“銷(xiāo)售額前三字典{top_3_dict}“) # {‘商品D‘: 45000, ‘商品B‘: 23000, ‘商品A‘: 15000}6. 常見(jiàn)“坑點(diǎn)”與調(diào)試技巧實(shí)錄在實(shí)際使用字典時(shí)尤其是初學(xué)者很容易掉進(jìn)一些陷阱。下面是我總結(jié)的幾個(gè)高頻問(wèn)題和解決方法。6.1 KeyError如何優(yōu)雅地處理鍵不存在這是最常見(jiàn)的錯(cuò)誤沒(méi)有之一。問(wèn)題場(chǎng)景config {‘host‘: ‘localhost‘, ‘port‘: 8080} print(config[‘timeout‘]) # KeyError: ‘timeout‘解決方案使用get()方法這是最簡(jiǎn)潔安全的方式。timeout config.get(‘timeout‘) # 不存在則返回None timeout config.get(‘timeout‘, 30) # 不存在則返回默認(rèn)值30使用in操作符預(yù)先判斷if ‘timeout‘ in config: timeout config[‘timeout‘] else: timeout 30使用setdefault()如果你希望在鍵不存在時(shí)不僅返回值還要在字典中設(shè)置它就用這個(gè)方法。# 確?!畉imeout‘鍵存在并獲取其值 timeout config.setdefault(‘timeout‘, 30)實(shí)操心得在編寫(xiě)通用函數(shù)或處理外部輸入如JSON、用戶(hù)輸入構(gòu)建的字典時(shí)養(yǎng)成使用get()的習(xí)慣能極大增強(qiáng)代碼的健壯性。6.2 遍歷時(shí)修改字典導(dǎo)致的RuntimeError問(wèn)題場(chǎng)景d {‘a(chǎn)‘: 1, ‘b‘: 2, ‘c‘: 3} for key in d: if key ‘b‘: del d[key] # RuntimeError: dictionary changed size during iteration解決方案 在遍歷時(shí)不能直接增刪字典的鍵修改值通常沒(méi)問(wèn)題。你需要先收集要處理的鍵遍歷結(jié)束后再操作。d {‘a(chǎn)‘: 1, ‘b‘: 2, ‘c‘: 3} keys_to_delete [] for key in d: if key ‘b‘: keys_to_delete.append(key) for key in keys_to_delete: del d[key] print(d) # {‘a(chǎn)‘: 1, ‘c‘: 3}或者遍歷字典的鍵的副本for key in list(d.keys()): # 用list()創(chuàng)建鍵的副本 if key ‘b‘: del d[key]6.3 可變對(duì)象作為鍵的陷阱問(wèn)題場(chǎng)景 試圖將列表作為字典的鍵。try: my_dict {[1, 2]: ‘value‘} except TypeError as e: print(e) # unhashable type: ‘list‘解決方案 如果確實(shí)需要用一個(gè)序列作為鍵可以將其轉(zhuǎn)換為不可變的元組。my_dict {tuple([1, 2]): ‘value‘} # 使用元組作為鍵 print(my_dict[(1, 2)]) # 輸出value但要注意如果元組內(nèi)包含可變對(duì)象如列表它仍然是不可哈希的。# 以下仍然會(huì)報(bào)錯(cuò) # bad_tuple (1, [2, 3]) # my_dict {bad_tuple: ‘value‘} # TypeError6.4 字典相等性比較的細(xì)節(jié)兩個(gè)字典相等的條件是它們有相同的鍵值對(duì)。但順序不影響相等性判斷Python 3.6以后即使順序不同只要鍵值對(duì)相同也返回True。dict1 {‘a(chǎn)‘: 1, ‘b‘: 2} dict2 {‘b‘: 2, ‘a(chǎn)‘: 1} print(dict1 dict2) # True但是如果你需要嚴(yán)格比較順序在某些特定場(chǎng)景下可以使用collections.OrderedDict或者將items()轉(zhuǎn)換為列表后比較。7. 性能優(yōu)化與最佳實(shí)踐7.1 使用collections模塊中的高級(jí)字典Python標(biāo)準(zhǔn)庫(kù)的collections模塊提供了幾種增強(qiáng)型的字典能解決特定場(chǎng)景下的痛點(diǎn)。defaultdict為不存在的鍵提供默認(rèn)值。 在統(tǒng)計(jì)頻率、構(gòu)建分組等場(chǎng)景下可以省去setdefault或get的判斷讓代碼更簡(jiǎn)潔。from collections import defaultdict # 默認(rèn)值為0的字典 word_count defaultdict(int) # int()的默認(rèn)值是0 for word in [‘a(chǎn)‘, ‘b‘, ‘a(chǎn)‘, ‘c‘]: word_count[word] 1 # 即使‘a(chǎn)‘第一次出現(xiàn)也會(huì)自動(dòng)初始化為0 print(dict(word_count)) # {‘a(chǎn)‘: 2, ‘b‘: 1, ‘c‘: 1} # 默認(rèn)值為空列表的字典 groups defaultdict(list) groups[‘fruit‘].append(‘a(chǎn)pple‘) groups[‘fruit‘].append(‘banana‘) print(dict(groups)) # {‘fruit‘: [‘a(chǎn)pple‘, ‘banana‘]}Counter專(zhuān)為計(jì)數(shù)設(shè)計(jì)的字典子類(lèi)。 它是defaultdict(int)的強(qiáng)化版提供了像most_common(n)這樣直接獲取最常見(jiàn)元素的方法。from collections import Counter words [‘a(chǎn)pple‘, ‘banana‘, ‘a(chǎn)pple‘, ‘orange‘, ‘banana‘, ‘a(chǎn)pple‘] word_counter Counter(words) print(word_counter) # Counter({‘a(chǎn)pple‘: 3, ‘banana‘: 2, ‘orange‘: 1}) print(word_counter.most_common(2)) # [(‘a(chǎn)pple‘, 3), (‘banana‘, 2)]OrderedDict記住鍵插入順序的字典。 在Python 3.7之前普通dict不保證順序OrderedDict是必須的。3.7之后雖然dict有序了但OrderedDict在相等性比較時(shí)考慮順序并且有move_to_end()等特有方法。7.2 字典推導(dǎo)式的性能優(yōu)勢(shì)在創(chuàng)建新字典時(shí)如果邏輯是簡(jiǎn)單的映射或過(guò)濾字典推導(dǎo)式通常比循環(huán)賦值更快也更符合Python的“優(yōu)雅”哲學(xué)。# 傳統(tǒng)方式 squares {} for x in range(10): squares[x] x**2 # 字典推導(dǎo)式更優(yōu) squares {x: x**2 for x in range(10)}對(duì)于復(fù)雜邏輯如果可讀性受影響則不必強(qiáng)求使用推導(dǎo)式。7.3 判斷字典是否為空的正確方式不要用if len(my_dict) 0:更不要用if my_dict {}:。最Pythonic的方式是直接利用字典在布爾上下文中的行為空字典為False非空為T(mén)rue。my_dict {} if not my_dict: # 正確且高效 print(“字典是空的“) if my_dict: # 非空時(shí)執(zhí)行 print(“字典有內(nèi)容“)字典是Python的基石之一理解其原理并熟練運(yùn)用能讓你寫(xiě)出更高效、更優(yōu)雅的代碼。從“頭歌”的實(shí)驗(yàn)題出發(fā)多思考、多練習(xí)把字典的鍵值對(duì)思維應(yīng)用到各種實(shí)際場(chǎng)景中你會(huì)發(fā)現(xiàn)很多復(fù)雜問(wèn)題都迎刃而解了。比如用字典緩存函數(shù)計(jì)算結(jié)果備忘錄技術(shù)用字典管理程序的狀態(tài)機(jī)用嵌套字典解析復(fù)雜的JSON API響應(yīng)等等。掌握它絕對(duì)物超所值。