使用 Python defaultdict 類型處理丟失的鍵(使用驅動器u盤之前需要格式化)

      網友投稿 1301 2022-05-30

      目錄

      處理字典中的缺失鍵

      了解 Python defaultdict 類型

      使用 Python defaultdict 類型

      分組項目

      對唯一項進行分組

      計數項目

      累積值

      深入了解 defaultdict

      defaultdict 與 dict

      使用 Python defaultdict 類型處理丟失的鍵(使用驅動器u盤之前需要格式化)

      defaultdict.default_factory

      defaultdict 與 dict.setdefault()

      defaultdict.__missing__()

      模擬 Python defaultdict 類型

      將參數傳遞給 .default_factory

      使用 lambda

      使用 functools.partial()

      結論

      使用 Python字典時可能會遇到的一個常見問題是嘗試訪問或修改字典中不存在的鍵。這將引發 aKeyError并中斷您的代碼執行。為了處理這些情況,標準庫提供了 Pythondefaultdict類型,這是一個類似字典的類,在collections.

      Pythondefaultdict類型的行為幾乎與常規 Python 字典完全相同,但是如果您嘗試訪問或修改缺少的鍵,defaultdict則將自動創建該鍵并為其生成默認值。這defaultdict為處理字典中丟失的鍵提供了一個有價值的選擇。

      在本教程中,您將學習:

      如何使用 Pythondefaultdict類型處理字典中缺失的鍵

      何時以及為何使用 Pythondefaultdict而不是常規dict

      如何使用 adefaultdict進行分組、計數和累加操作

      掌握這些知識后,您將能夠更好地defaultdict在日常編程挑戰中有效地使用 Python類型。

      為了充分利用本教程,您應該事先了解 Python詞典是什么以及如何使用它們。如果您需要煥然一新,請查看以下資源:

      Python 中的字典(教程)

      Python 中的字典(課程)

      如何在 Python 中遍歷字典

      處理字典中的缺失鍵

      使用 Python 字典時可能面臨的一個常見問題是如何處理丟失的鍵。如果您的代碼很大程度上基于字典,或者您一直在動態創建字典,那么您很快就會注意到處理頻繁的KeyError異??赡軙浅┤?,并且會給您的代碼增加額外的復雜性。使用 Python 字典,您至少有四種可用的方法來處理丟失的鍵:

      利用?.setdefault()

      利用?.get()

      使用key in dict成語

      使用 atry和except塊

      在Python文檔說明.setdefault()和.get()如下:

      setdefault(key[, default])

      如果key在字典中,則返回其值。如果不是,則插入key值為default并返回default。default默認為None.

      get(key[, default])

      返回keyifkey在字典中的值, else?default。如果default未給出,則默認為None,因此此方法永遠不會引發KeyError。

      (來源)

      下面是一個如何.setdefault()處理字典中缺失鍵的示例:

      >>>

      >>> a_dict = {} >>> a_dict['missing_key'] Traceback (most recent call last): File "", line 1, in a_dict['missing_key'] KeyError: 'missing_key' >>> a_dict.setdefault('missing_key', 'default value') 'default value' >>> a_dict['missing_key'] 'default value' >>> a_dict.setdefault('missing_key', 'another default value') 'default value' >>> a_dict {'missing_key': 'default value'}

      在上面的代碼中,您用于.setdefault()為 生成默認值missing_key。請注意,您的字典 ,a_dict現在有一個名為missing_key的新鍵,其值為'default value'。在您調用 之前,此密鑰不存在.setdefault()。最后,如果您調用.setdefault()現有的鍵,則調用不會對字典產生任何影響。您的密鑰將保存原始值而不是新的默認值。

      注意:在上面的代碼示例中,您會收到一個異常,并且 Python 會向您顯示一條回溯消息,該消息告訴您您正在嘗試訪問a_dict.?如果您想更深入地了解如何破譯和理解 Python 回溯,請查看了解 Python 回溯和充分利用 Python 回溯。

      另一方面,如果您使用.get(),那么您可以編寫如下代碼:

      >>>

      >>> a_dict = {} >>> a_dict.get('missing_key', 'default value') 'default value' >>> a_dict {}

      在這里,您使用.get()為 生成默認值missing_key,但這次,您的字典保持為空。這是因為.get()返回默認值,但此值未添加到基礎字典中。例如,如果您有一本名為 的字典D,那么您可以假設它的.get()工作原理如下:

      D.get(key, default) -> D[key] if key in D, else default

      使用此偽代碼,您可以了解.get()內部是如何工作的。如果鍵存在,則.get()返回映射到該鍵的值。否則,返回默認值。您的代碼永遠不會創建或分配值key。在本例中,default默認為None。

      您還可以使用條件語句來處理字典中缺失的鍵??纯聪旅娴睦樱褂昧诉@個key in dict習語:

      >>>

      >>> a_dict = {} >>> if 'key' in a_dict: ... # Do something with 'key'... ... a_dict['key'] ... else: ... a_dict['key'] = 'default value' ... >>> a_dict {'key': 'default value'}

      在此代碼,您使用的if語句與一起in操作,以檢查是否key存在a_dict。如果是這樣,那么您可以使用key或使用其值執行任何操作。否則,您將創建新密鑰key,并為其分配一個'default value'。請注意,上述代碼的工作原理類似于.setdefault()但需要四行代碼,而.setdefault()只需要一行(除了更具可讀性之外)。

      您還可以KeyError使用 atry和except塊來處理異常。考慮下面的一段代碼:

      >>>

      >>> a_dict = {} >>> try: ... # Do something with 'key'... ... a_dict['key'] ... except KeyError: ... a_dict['key'] = 'default value' ... >>> a_dict {'key': 'default value'}

      上面示例中的tryandexcept塊會在KeyError您嘗試訪問丟失的密鑰時捕獲。在except子句中,您創建key并為其分配一個'default value'。

      注意:如果缺少的鍵在您的代碼中并不常見,那么您可能更喜歡使用 atry和except塊(EAFP 編碼風格)來捕獲KeyError異常。這是因為代碼不會檢查每個鍵是否存在,并且只處理少數異常(如果有)。

      另一方面,如果缺少鍵在您的代碼中很常見,那么條件語句(LBYL 編碼風格)可能是更好的選擇,因為檢查鍵的成本比處理頻繁異常的成本更低。

      到目前為止,您已經學會了如何使用dictPython 提供的工具處理丟失的鍵。但是,您在此處看到的示例非常冗長且難以閱讀。它們可能不像您想要的那么簡單。這就是Python 標準庫提供更優雅、Pythonic和高效的解決方案的原因。該解決方案是collections.defaultdict,這就是您從現在開始要介紹的內容。

      理解 Pythondefaultdict類型

      Python 標準庫提供了collections,它是一個實現專用容器類型的模塊。其中之一是 Pythondefaultdict類型,它是dict專為幫助您解決丟失鍵而設計的替代方法。defaultdict是一種繼承自的 Python 類型dict:

      >>>

      >>> from collections import defaultdict >>> issubclass(defaultdict, dict) True

      上面的代碼顯示,Python的defaultdict類型是子類的dict。這意味著defaultdict繼承了 的大部分行為dict。所以,你可以說這defaultdict很像一本普通的字典。

      defaultdict和之間的主要區別在于dict,當您嘗試訪問或修改key字典中不存在的 時,value會自動為該提供默認值key。為了提供這個功能,Pythondefaultdict類型做了兩件事:

      它覆蓋.__missing__().

      它添加了.default_factory一個需要在實例化時提供的可寫實例變量。

      實例變量.default_factory將保存傳入的第一個參數defaultdict.__init__()。此參數可以采用有效的 Python 可調用或None.?如果提供了可調用對象,則defaultdict每當您嘗試訪問或修改與缺失鍵關聯的值時,它都會自動被調用。

      注意:類初始值設定項的所有剩余參數都被視為傳遞給常規的初始值設定項dict,包括關鍵字參數。

      看看如何創建和正確初始化 a?defaultdict:

      >>>

      >>> # Correct instantiation >>> def_dict = defaultdict(list) # Pass list to .default_factory >>> def_dict['one'] = 1 # Add a key-value pair >>> def_dict['missing'] # Access a missing key returns an empty list [] >>> def_dict['another_missing'].append(4) # Modify a missing key >>> def_dict defaultdict(, {'one': 1, 'missing': [], 'another_missing': [4]})

      在這里,您在創建字典時傳遞list到.default_factory。然后,您def_dict就像使用普通字典一樣使用。請注意,當您嘗試訪問或修改映射到不存在的鍵的值時,字典會為其分配調用list().

      請記住,您必須將有效的 Python 可調用對象傳遞給.default_factory,因此請記住不要在初始化時使用括號調用它。當您開始使用 Pythondefaultdict類型時,這可能是一個常見問題。看看下面的代碼:

      >>>

      >>> # Wrong instantiation >>> def_dict = defaultdict(list()) Traceback (most recent call last): File "", line 1, in def_dict = defaultdict(list()) TypeError: first argument must be callable or None

      在這里,您嘗試defaultdict通過傳遞list()給.default_factory.?對 的調用list()引發 a?TypeError,它告訴您第一個參數必須是可調用的 or?None。

      通過對 Pythondefaultdict類型的介紹,您可以通過實際示例開始編碼。接下來的幾節將帶您了解一些常見的用例,您可以在這些用例中依靠 adefaultdict提供優雅、高效和 Pythonic 的解決方案。

      使用 Pythondefaultdict類型

      有時候,你會使用內置集合一個可變的(一個list,dict或set)在你的Python字典值。在這些情況下,您需要在第一次使用前初始化密鑰,否則您會得到一個KeyError.?您可以手動執行此過程,也可以使用 Python 自動執行此過程defaultdict。在本節中,您將學習如何使用 Pythondefaultdict類型來解決一些常見的編程問題:

      將集合中的項目分組

      計算集合中的項目

      累積集合中的值

      您將介紹一些使用list、set、int和float以用戶友好且高效的方式執行分組、計數和累加操作的示例。

      分組項目

      Pythondefaultdict類型的典型用途是設置.default_factory為list然后構建一個將鍵映射到值列表的字典。有了這個defaultdict,如果您嘗試訪問任何丟失的鍵,則字典將運行以下步驟:

      調用?list()創建一個新的空list

      插入空的list使用缺少鍵進入詞典key

      返回對那個的引用list

      這允許您編寫如下代碼:

      >>>

      >>> from collections import defaultdict >>> dd = defaultdict(list) >>> dd['key'].append(1) >>> dd defaultdict(, {'key': [1]}) >>> dd['key'].append(2) >>> dd defaultdict(, {'key': [1, 2]}) >>> dd['key'].append(3) >>> dd defaultdict(, {'key': [1, 2, 3]})

      在這里,您創建一個defaultdict名為的 Pythondd并傳遞list給.default_factory.?請注意,即使key沒有定義,你可以追加值,它沒有得到一個KeyError。那是因為dd自動調用.default_factory為缺少的key.

      您可以使用defaultdictwithlist對序列或集合中的項目進行分組。假設您已從公司的數據庫中檢索到以下數據:

      有了這些數據,您創建初始list的tuple像下列對象:

      dep = [('Sales', 'John Doe'), ('Sales', 'Martin Smith'), ('Accounting', 'Jane Doe'), ('Marketing', 'Elizabeth Smith'), ('Marketing', 'Adam Doe')]

      現在,您需要創建一個按部門對員工進行分組的字典。為此,您可以使用 adefaultdict如下:

      from collections import defaultdict dep_dd = defaultdict(list) for department, employee in dep: dep_dd[department].append(employee)

      在這里,您創建一個defaultdict被調用的對象dep_dd并使用for循環來遍歷您的dep列表。該語句dep_dd[department].append(employee)為部門創建鍵,將它們初始化為一個空列表,然后將員工附加到每個部門。運行此代碼后,您dep_dd將看起來像這樣:

      >>>

      defaultdict(, {'Sales': ['John Doe', 'Martin Smith'], 'Accounting' : ['Jane Doe'], 'Marketing': ['Elizabeth Smith', 'Adam Doe']})

      在此示例中,您將使用defaultdictwith.default_factory設置為按部門對員工進行分組list。要使用常規字典執行此操作,您可以使用dict.setdefault()以下方法:

      dep_d = dict() for department, employee in dep: dep_d.setdefault(department, []).append(employee)

      這段代碼很簡單,作為 Python 編碼員,您在工作中經常會發現類似的代碼。然而,該defaultdict版本可以說更具可讀性,而且對于大型數據集,它也可以更快、更高效。因此,如果您關心速度,那么您應該考慮使用 adefaultdict而不是標準dict.

      對唯一項進行分組

      繼續使用上一節中的部門和員工數據。經過一些處理,您發現數據庫中錯誤地復制了一些員工。您需要清理數據并從dep_dd字典中刪除重復的員工。為此,您可以使用 a?setas the.default_factory并按如下方式重寫您的代碼:

      dep = [('Sales', 'John Doe'), ('Sales', 'Martin Smith'), ('Accounting', 'Jane Doe'), ('Marketing', 'Elizabeth Smith'), ('Marketing', 'Elizabeth Smith'), ('Marketing', 'Adam Doe'), ('Marketing', 'Adam Doe'), ('Marketing', 'Adam Doe')] dep_dd = defaultdict(set) for department, employee in dep: dep_dd[department].add(employee)

      在本例中,您設置.default_factory為set。集合是唯一對象的集合,這意味著您不能創建set具有重復項的集合。這是集合的一個非常有趣的特性,它保證您在最終字典中不會有重復的項目。

      計數項目

      如果您設置.default_factory為int,那么您defaultdict將有助于計算序列或集合中的項目。當您int()不帶參數調用時,該函數返回0,這是您用來初始化計數器的典型值。

      繼續以公司數據庫為例,假設您要構建一個字典來計算每個部門的員工人數。在這種情況下,您可以編寫如下代碼:

      >>>

      >>> from collections import defaultdict >>> dep = [('Sales', 'John Doe'), ... ('Sales', 'Martin Smith'), ... ('Accounting', 'Jane Doe'), ... ('Marketing', 'Elizabeth Smith'), ... ('Marketing', 'Adam Doe')] >>> dd = defaultdict(int) >>> for department, _ in dep: ... dd[department] += 1 >>> dd defaultdict(, {'Sales': 2, 'Accounting': 1, 'Marketing': 2})

      在這里,您設置.default_factory為int。int()不帶參數調用時,返回值為0。您可以使用此默認值開始計算在每個部門工作的員工數。要使此代碼正常工作,您需要一個干凈的數據集。不得有重復數據。否則,您需要過濾掉重復的員工。

      另一個計算項目的例子是計算mississippi一個單詞中每個字母重復的次數??纯聪旅娴拇a:

      >>>

      >>> from collections import defaultdict >>> s = 'mississippi' >>> dd = defaultdict(int) >>> for letter in s: ... dd[letter] += 1 ... >>> dd defaultdict(, {'m': 1, 'i': 4, 's': 4, 'p': 2})

      在上面的代碼中,您創建了一個defaultdictwith?.default_factoryset to?int。這將任何給定鍵的默認值設置為0。然后,您使用for循環遍歷字符串?s并使用增強賦值操作1在每次迭代中添加到計數器。ddwill的鍵是 中的字母mississippi。

      注意:?Python 的增強賦值運算符是常見操作的便捷快捷方式。

      看看下面的例子:

      var += 1?相當于?var = var + 1

      var -= 1?相當于?var = var - 1

      var *= 1?相當于?var = var * 1

      這只是增強賦值運算符如何工作的一個示例。您可以查看官方文檔以了解有關此功能的更多信息。

      由于計數是編程中相對常見的任務,Python 類字典類collections.Counter專門用于對序列中的項目進行計數。使用Counter,您可以編寫mississippi如下示例:

      >>>

      >>> from collections import Counter >>> counter = Counter('mississippi') >>> counter Counter({'i': 4, 's': 4, 'p': 2, 'm': 1})

      在這種情況下,Counter為您完成所有工作!你只需要傳入一個序列,字典就會計算它的項目,將它們存儲為鍵,將計數存儲為值。請注意,此示例有效,因為 Python 字符串也是序列類型。

      累積值

      有時您需要計算序列或集合中值的總和。假設您有以下Excel 表格,其中包含有關 Python 網站銷售的數據:

      接下來,您可以使用Python和得到如下處理數據list的tuple對象:

      incomes = [('Books', 1250.00), ('Books', 1300.00), ('Books', 1420.00), ('Tutorials', 560.00), ('Tutorials', 630.00), ('Tutorials', 750.00), ('Courses', 2500.00), ('Courses', 2430.00), ('Courses', 2750.00),]

      使用此數據,您希望計算每個產品的總收入。為此,您可以使用defaultdict帶有floatas的 Python?,.default_factory然后編寫如下代碼:

      1from collections import defaultdict 2 3dd = defaultdict(float) 4for product, income in incomes: 5 dd[product] += income 6 7for product, income in dd.items(): 8 print(f'Total income for {product}: ${income:,.2f}')

      下面是這段代碼的作用:

      在第 1 行中,您導入 Pythondefaultdict類型。

      在第 3 行中,您創建了一個設置為的defaultdict對象。.default_factoryfloat

      在第 4 行中,您定義了一個for循環來遍歷 的項目incomes。

      在第 5 行中,您使用增廣賦值操作 (?+=) 來累積字典中每個產品的收入。

      第二個循環遍歷 的項目dd并將收入打印到您的屏幕上。

      注意:如果您想更深入地了解字典迭代,請查看如何在 Python 中迭代字典。

      如果您將所有這些代碼放入一個名為的文件中incomes.py并從命令行運行它,那么您將獲得以下輸出:

      $ python3 incomes.py Total income for Books: $3,970.00 Total income for Tutorials: $1,940.00 Total income for Courses: $7,680.00

      您現在擁有每個產品的收入摘要,因此您可以決定遵循哪種策略來增加網站的總收入。

      深入了解?defaultdict

      到目前為止,您已經defaultdict通過編寫一些實際示例學習了如何使用 Python類型。此時,您可以更深入地了解類型實現和其他工作細節。這就是您將在接下來的幾節中介紹的內容。

      defaultdict?對比?dict

      為了更好地理解 Pythondefaultdict類型,一個很好的練習是將它與其超類dict.?如果您想知道特定于 Pythondefaultdict類型的方法和屬性,則可以運行以下代碼行:

      >>>

      >>> set(dir(defaultdict)) - set(dir(dict)) {'__copy__', 'default_factory', '__missing__'}

      在上面的代碼,你可以使用dir()以獲取有效的屬性列表dict和defaultdict。然后,您使用set差異來獲取只能在defaultdict.?如您所見,這兩個類之間的區別是。您有兩種方法和一種實例屬性。下表顯示了方法和屬性的用途:

      在上表中,你可以看到,做的方法和屬性defaultdict從一個普通的不同dict。其余的方法在兩個類中都是相同的。

      注意:如果您defaultdict使用有效的可調用對象初始化 a?,那么KeyError當您嘗試訪問丟失的密鑰時,您將不會得到 a?。任何不存在的鍵都會獲得由 返回的值.default_factory。

      此外,您可能會注意到 adefaultdict等于dict具有相同項的 a:

      >>>

      >>> std_dict = dict(numbers=[1, 2, 3], letters=['a', 'b', 'c']) >>> std_dict {'numbers': [1, 2, 3], 'letters': ['a', 'b', 'c']} >>> def_dict = defaultdict(list, numbers=[1, 2, 3], letters=['a', 'b', 'c']) >>> def_dict defaultdict(, {'numbers': [1, 2, 3], 'letters': ['a', 'b', 'c']}) >>> std_dict == def_dict True

      在這里,您創建一個std_dict包含一些任意項目的常規字典。然后,您defaultdict使用相同的項目創建一個。如果您測試兩個詞典的內容是否相等,那么您會發現它們是相等的。

      defaultdict.default_factory

      Pythondefaultdict類型的第一個參數必須是一個不帶參數并返回一個值的可調用對象。此參數分配給實例屬性,.default_factory。為此,您可以使用任何可調用對象,包括函數、方法、類、類型對象或任何其他有效的可調用對象。默認值.default_factory是None。

      如果您在defaultdict不將值傳遞給 的情況下進行實例化.default_factory,則字典的行為將與常規類似dict,并且通常KeyError會因缺少鍵查找或修改嘗試而引發:

      >>>

      >>> from collections import defaultdict >>> dd = defaultdict() >>> dd['missing_key'] Traceback (most recent call last): File "", line 1, in dd['missing_key'] KeyError: 'missing_key'

      在這里,您將defaultdict不帶參數地實例化 Python類型。在這種情況下,實例的行為類似于標準字典。因此,如果您嘗試訪問或修改丟失的密鑰,那么您將獲得通常的KeyError.?從現在開始,您可以將其dd用作普通的 Python 字典,除非您將新的可調用對象分配給 ,否則.default_factory您將無法使用defaultdict自動處理丟失的鍵的功能。

      如果您傳遞None給 的第一個參數defaultdict,則該實例的行為與您在上述示例中看到的相同。那是因為.default_factory默認為None,所以兩個初始化是等效的。另一方面,如果將有效的可調用對象傳遞給.default_factory,則可以使用它以用戶友好的方式處理丟失的鍵。這是您傳遞list給的示例.default_factory:

      >>>

      >>> dd = defaultdict(list, letters=['a', 'b', 'c']) >>> dd.default_factory >>> dd defaultdict(, {'letters': ['a', 'b', 'c']}) >>> dd['numbers'] [] >>> dd defaultdict(, {'letters': ['a', 'b', 'c'], 'numbers': []}) >>> dd['numbers'].append(1) >>> dd defaultdict(, {'letters': ['a', 'b', 'c'], 'numbers': [1]}) >>> dd['numbers'] += [2, 3] >>> dd defaultdict(, {'letters': ['a', 'b', 'c'], 'numbers': [1, 2, 3]})

      在此示例中,您創建了一個defaultdict名為的 Python?dd,然后將list其用作第一個參數。調用第二個參數letters并保存一個字母列表。您會看到,它.default_factory現在擁有一個list對象,當您需要value為任何丟失的鍵提供默認值時將調用該對象。

      請注意,當您嘗試訪問時numbers,會dd測試是否numbers在字典中。如果不是,則調用.default_factory().?由于.default_factory持有一個list對象,返回的value是一個空列表([])。

      現在dd['numbers']已用空初始化list,您可以使用.append()將元素添加到list.?您還可以使用增強賦值運算符 (?+=) 來連接列表[1]和[2, 3]。這樣,您可以以更 Pythonic 和更有效的方式處理丟失的鍵。

      另一方面,如果您將不可調用的對象傳遞給Pythondefaultdict類型的初始化程序,那么您將TypeError在以下代碼中得到類似的結果:

      >>>

      >>> defaultdict(0) Traceback (most recent call last): File "", line 1, in defaultdict(0) TypeError: first argument must be callable or None

      在這里,您傳遞0到.default_factory。由于0不是可調用對象,您會得到一個TypeError告訴您第一個參數必須是可調用的或None.?否則,defaultdict不起作用。

      請記住,.default_factory它僅從.__getitem__()其他方法調用,而不是從其他方法調用。這意味著 if?ddis a?defaultdictand?keyis a missing key, then?dd[key]will call?.default_factoryto provide a default?value,但dd.get(key)仍然返回None而不是提供的值.default_factory。那是因為.get()沒有調用.__getitem__()來檢索key.

      看看下面的代碼:

      >>>

      >>> dd = defaultdict(list) >>> # Calls dd.__getitem__('missing') >>> dd['missing'] [] >>> # Don't call dd.__getitem__('another_missing') >>> print(dd.get('another_missing')) None >>> dd defaultdict(, {'missing': []})

      在此代碼片段中,您可以看到dd.get()返回None值而不是提供的默認值.default_factory。那是因為.default_factory只調用 from?.__missing__(),而不調用.get().

      請注意,您還可以向 Python 中添加任意值defaultdict。這意味著您不限于與由 生成的值具有相同類型的值.default_factory。下面是一個例子:

      >>>

      >>> dd = defaultdict(list) >>> dd defaultdict(, {}) >>> dd['string'] = 'some string' >>> dd defaultdict(, {'string': 'some string'}) >>> dd['list'] [] >>> dd defaultdict(, {'string': 'some string', 'list': []})

      在這里,您創建 adefaultdict并將list對象傳遞給.default_factory。這會將您的默認值設置為空列表。但是,您可以自由添加包含不同類型值的新鍵。key 就是這種情況string,它保存一個str對象而不是一個list對象。

      最后,您始終可以像處理任何實例屬性一樣更改或更新最初分配給的可調用對象.default_factory:

      >>>

      >>> dd.default_factory = str >>> dd['missing_key'] ''

      在上面的代碼中,您.default_factory從更改list為str?,F在,每當您嘗試訪問丟失的密鑰時,您的默認值將是一個空字符串 (?'')。

      根據您對 Pythondefaultdict類型的使用案例,您可能需要在完成創建后凍結字典并將其設為只讀。為此,您可以在完成字典填充后設置.default_factory為None。這樣,您的字典將表現得像一個標準dict,這意味著您將不會有更多自動生成的默認值。

      defaultdict?對比?dict.setdefault()

      正如您之前看到的,dict提供了.setdefault(),它允許您即時為缺失的鍵分配值。相反,使用 adefaultdict可以在初始化容器時預先指定默認值。您可以使用.setdefault()來分配默認值,如下所示:

      >>>

      >>> d = dict() >>> d.setdefault('missing_key', []) [] >>> d {'missing_key': []}

      在此代碼中,您創建了一個常規字典,然后使用它為尚未定義的鍵.setdefault()分配一個值 (?[])?missing_key。

      注意:您可以使用.setdefault().?與defaultdict您認為defaultdict僅接受可調用或None.

      另一方面,如果您使用 adefaultdict來完成相同的任務,那么每當您嘗試訪問或修改丟失的密鑰時,都會按需生成默認值。請注意,使用defaultdict,默認值是由您預先傳遞給類的初始化程序的可調用對象生成的。這是它的工作原理:

      >>>

      >>> from collections import defaultdict >>> dd = defaultdict(list) >>> dd['missing_key'] [] >>> dd defaultdict(, {'missing_key': []})

      在這里,您首先defaultdict從collections.?然后,您創建一個defaultdict并傳遞list給.default_factory.?當您嘗試訪問丟失的鍵時,在defaultdict內部調用.default_factory(),它保存對 的引用list,并將結果值(空的list)分配給missing_key。

      上面兩個示例中的代碼執行相同的工作,但該defaultdict版本可以說更具可讀性、用戶友好性、Pythonic 和簡單明了。

      注意:調用內置類型如list,?set,?dict,?str,?int, orfloat將返回一個空對象或數字類型的零。

      看看下面的代碼示例:

      >>>

      >>> list() [] >>> set() set([]) >>> dict() {} >>> str() '' >>> float() 0.0 >>> int() 0

      在此代碼中,您調用一些沒有參數的內置類型,并為數字類型獲取一個空對象或零。

      最后,使用 adefaultdict來處理丟失的鍵可能比使用dict.setdefault().?看看下面的例子:

      # Filename: exec_time.py from collections import defaultdict from timeit import timeit animals = [('cat', 1), ('rabbit', 2), ('cat', 3), ('dog', 4), ('dog', 1)] std_dict = dict() def_dict = defaultdict(list) def group_with_dict(): for animal, count in animals: std_dict.setdefault(animal, []).append(count) return std_dict def group_with_defaultdict(): for animal, count in animals: def_dict[animal].append(count) return def_dict print(f'dict.setdefault() takes {timeit(group_with_dict)} seconds.') print(f'defaultdict takes {timeit(group_with_defaultdict)} seconds.')

      如果您從系統的命令行運行腳本,那么您將得到如下內容:

      $ python3 exec_time.py dict.setdefault() takes 1.0281260240008123 seconds. defaultdict takes 0.6704721650003194 seconds.

      在這里,您可以使用timeit.timeit()來衡量的執行時間group_with_dict()和group_with_defaultdict()。這些函數執行等效的操作,但第一個使用dict.setdefault(),第二個使用defaultdict。時間度量將取決于您當前的硬件,但您可以在此處看到它defaultdict比dict.setdefault().?隨著數據集變大,這種差異會變得更加重要。

      此外,您需要考慮到創建常規dict可能比創建defaultdict.?看看這段代碼:

      >>>

      >>> from timeit import timeit >>> from collections import defaultdict >>> print(f'dict() takes {timeit(dict)} seconds.') dict() takes 0.08921320698573254 seconds. >>> print(f'defaultdict() takes {timeit(defaultdict)} seconds.') defaultdict() takes 0.14101867799763568 seconds.

      這一次,你timeit.timeit()用來衡量dict和defaultdict實例化的執行時間。請注意,創建 adict幾乎是創建 a 的一半時間defaultdict。如果您考慮到在實際代碼中通常defaultdict只實例化一次,這可能不是問題。

      另請注意,默認情況下,timeit.timeit()將運行您的代碼一百萬次。這就是定義std_dict和def_dict超出group_with_dict()和group_with_defaultdict()in范圍的原因exec_time.py。否則,時間測量將通過實例化時間的影響dict和defaultdict。

      此時,您可能知道何時使用 adefaultdict而不是常規dict.?以下是需要考慮的三件事:

      如果您的代碼在很大程度上基于字典并且您一直在處理丟失的鍵,那么您應該考慮使用.defaultdict而不是常規的dict.

      如果您的字典項需要使用常量默認值初始化,那么您應該考慮使用 adefaultdict而不是 a?dict。

      如果您的代碼依賴字典來聚合、累加、計數或分組值,并且性能是一個問題,那么您應該考慮使用defaultdict.

      在決定使用 adict還是 a時,您可以考慮上述準則defaultdict。

      defaultdict.__missing__()

      在幕后,Pythondefaultdict類型通過調用.default_factory為缺少的鍵提供默認值來工作。使這成為可能的機制是.__missing__(),所有標準映射類型都支持的特殊方法,包括dict和defaultdict。

      注意:注意.__missing__()由 自動調用.__getitem__()以處理丟失的鍵,.__getitem__()同時由 Python 自動調用以進行訂閱操作,例如d[key].

      那么,它是如何.__missing__()工作的呢?如果設置.default_factory為None,則以為參數.__missing__()引發 a?。否則,不帶參數調用以提供給定的默認值。這被插入到字典中并最終返回。如果調用引發異常,則該異常將原樣傳播。KeyErrorkey.default_factoryvaluekeyvalue.default_factory

      以下代碼顯示了一個可行的 Python 實現.__missing__():

      1def __missing__(self, key): 2 if self.default_factory is None: 3 raise KeyError(key) 4 if key not in self: 5 self[key] = self.default_factory() 6 return self[key]

      下面是這段代碼的作用:

      在第 1 行,您定義方法及其簽名。

      在第 2 行和第 3 行中,您測試是否.default_factory為None。如果是這樣,那么你提出一個KeyError與key作為參數。

      在第 4 行和第 5 行中,您檢查key字典中是否沒有 。如果不是,則調用.default_factory并將其返回值分配給key.

      在第 6 行,您key按預期返回。

      請記住,.__missing__()映射中的存在對其他查找鍵的方法的行為沒有影響,例如實現運算符的.get()or?。那是因為只有在字典中找不到請求時才會調用。無論返回或引發什么,然后由 返回或引發。.__contains__()in.__missing__().__getitem__()key.__missing__().__getitem__()

      既然您已經介紹了 的替代 Python 實現.__missing__(),那么嘗試defaultdict用一些 Python 代碼進行模擬將是一個很好的練習。這就是您將在下一節中執行的操作。

      模擬 Pythondefaultdict類型

      在本節中,您將編寫一個 Python 類,其行為與defaultdict.?為此,您將子類化collections.UserDict,然后添加.__missing__().?此外,您需要添加一個名為 的實例屬性.default_factory,它將保存可調用以按需生成默認值。這是一段模擬 Pythondefaultdict類型的大部分行為的代碼:

      1import collections 2 3class my_defaultdict(collections.UserDict): 4 def __init__(self, default_factory=None, *args, **kwargs): 5 super().__init__(*args, **kwargs) 6 if not callable(default_factory) and default_factory is not None: 7 raise TypeError('first argument must be callable or None') 8 self.default_factory = default_factory 9 10 def __missing__(self, key): 11 if self.default_factory is None: 12 raise KeyError(key) 13 if key not in self: 14 self[key] = self.default_factory() 15 return self[key]

      下面是這段代碼的工作原理:

      在第 1 行中,您導入collections以訪問UserDict.

      在第 3 行中,您創建了一個子類化的類UserDict。

      在第 4 行中,您定義了類初始值設定項.__init__()。此方法采用一個被default_factory調用的參數來保存您將用于生成默認值的可調用對象。請注意,default_factory默認為None,就像在defaultdict.?您還需要*args和**kwargs來模擬常規dict.

      在第 5 行,您調用超類.__init__()。這意味著,我們在調用UserDict.__init__()和傳遞*args,并**kwargs給它。

      在第 6 行,您首先檢查是否default_factory是有效的可調用對象。在這種情況下,您使用callable(object),它是一個內置函數,True如果object看起來是可調用的則返回,否則返回False。此檢查可確保您.default_factory()在需要value為任何缺失的key.?然后,您檢查是否.default_factory不是None。

      在第 7 行中,您提出 aTypeError就像普通人dict會做的 if?default_factoryis 一樣None。

      在第 8 行中,您初始化.default_factory.

      在第 10 行,您定義了.__missing__(),正如您之前看到的那樣實現?;叵胍幌拢斀o定的字典不在字典中時,它.__missing__()會自動調用。.__getitem__()key

      如果您有心情閱讀一些C代碼,那么您可以查看CPython 源代碼中Python類型的完整代碼。defaultdict

      現在您已經完成了這個類的編碼,您可以通過將代碼放入一個名為的 Python 腳本中my_dd.py并從交互式會話中導入它來測試它。下面是一個例子:

      >>>

      >>> from my_dd import my_defaultdict >>> dd_one = my_defaultdict(list) >>> dd_one {} >>> dd_one['missing'] [] >>> dd_one {'missing': []} >>> dd_one.default_factory = int >>> dd_one['another_missing'] 0 >>> dd_one {'missing': [], 'another_missing': 0} >>> dd_two = my_defaultdict(None) >>> dd_two['missing'] Traceback (most recent call last): File "", line 1, in dd_two['missing'] File "/home/user/my_dd.py", line 10, in __missing__ raise KeyError(key) KeyError: 'missing'

      在這里,您首先my_defaultdict從my_dd.?然后,您創建 的實例my_defaultdict并傳遞list給.default_factory。如果您嘗試通過訂閱操作訪問密鑰,例如dd_one['missing'],則.__getitem__()Python 會自動調用。如果鍵不在字典中,則.__missing__()調用它,它通過調用生成一個默認值.default_factory()。

      您還可以.default_factory使用正常的分配操作更改分配給的可調用對象,例如dd_one.default_factory = int。最后,如果您傳遞None給.default_factory,那么您將KeyError在嘗試檢索丟失的密鑰時得到 。

      注意: a的行為defaultdict本質上與此 Python 等效項相同。但是,您很快就會注意到您的 Python 實現不會打印為真實defaultdict的dict.?您可以通過覆蓋.__str__()和來修改此詳細信息.__repr__()。

      您可能想知道為什么在此示例中使用子類collections.UserDict而不是常規dict。這樣做的主要原因是對內置類型進行子類化可能容易出錯,因為內置類型的 C 代碼似乎不會始終如一地調用由用戶覆蓋的特殊方法。

      這是一個示例,顯示了在子類化時可能面臨的一些問題dict:

      >>>

      >>> class MyDict(dict): ... def __setitem__(self, key, value): ... super().__setitem__(key, None) ... >>> my_dict = MyDict(first=1) >>> my_dict {'first': 1} >>> my_dict['second'] = 2 >>> my_dict {'first': 1, 'second': None} >>> my_dict.setdefault('third', 3) 3 >>> my_dict {'first': 1, 'second': None, 'third': 3}

      在本例中,您創建MyDict,它是一個子類化 的類dict。您的實現.__setitem__()始終將值設置為None.?如果您創建一個實例MyDict并將關鍵字參數傳遞給它的初始值設定項,那么您會注意到該類沒有調用您.__setitem__()來處理分配。您知道這是因為first未分配密鑰None。

      相比之下,如果您運行像 那樣的訂閱操作my_dict['second'] = 2,那么您會注意到它second設置為None而不是2。所以,這一次您可以說訂閱操作調用您的自定義.__setitem__().?最后,請注意.setdefault()也不會調用.__setitem__(),因為您的third鍵最終的值為3。

      UserDict不繼承dict但模擬標準字典的行為。該類有一個dict名為的內部實例.data,用于存儲字典的內容。UserDict在創建自定義映射時是一個更可靠的類。如果您使用UserDict,那么您將避免之前看到的問題。為了證明這一點,回到代碼my_defaultdict并添加以下方法:

      1class my_defaultdict(collections.UserDict): 2 # Snip 3 def __setitem__(self, key, value): 4 print('__setitem__() gets called') 5 super().__setitem__(key, None)

      在這里,您添加一個.__setitem__()調用 superclass的自定義.__setitem__(),它始終將值設置為None。在您的腳本中更新此代碼my_dd.py并從交互式會話中導入它,如下所示:

      >>>

      >>> from my_dd import my_defaultdict >>> my_dict = my_defaultdict(list, first=1) __setitem__() gets called >>> my_dict {'first': None} >>> my_dict['second'] = 2 __setitem__() gets called >>> my_dict {'first': None, 'second': None}

      在這種情況下,當您實例化my_defaultdict并傳遞first給類初始值設定項時,您的自定義__setitem__()會被調用。此外,當您為 key 分配一個值時second,__setitem__()也會被調用。您現在有一個my_defaultdict始終調用您的自定義特殊方法的方法。請注意,字典中的所有值都等于None現在。

      將參數傳遞給?.default_factory

      正如您之前看到的,.default_factory必須設置為一個不帶參數并返回值的可調用對象。該值將用于為字典中任何缺失的鍵提供默認值。即使.default_factory不應該接受參數,Python 也提供了一些技巧,您可以在需要為其提供參數時使用這些技巧。在本節中,您將介紹兩種可用于此目的的 Python 工具:

      lambda

      functools.partial()

      使用這兩個工具,您可以為 Pythondefaultdict類型增加額外的靈活性。例如,您可以使用一個defaultdict帶有參數的可調用對象初始化 a?,經過一些處理后,您可以使用新參數更新可調用對象,以更改您將從此時起創建的鍵的默認值。

      使用?lambda

      將參數傳遞給的一種靈活方法.default_factory是使用lambda.?假設您要創建一個函數以在defaultdict.?該函數執行一些處理并返回一個值,但您需要傳遞一個參數才能使該函數正常工作。下面是一個例子:

      >>>

      >>> def factory(arg): ... # Do some processing here... ... result = arg.upper() ... return result ... >>> def_dict = defaultdict(lambda: factory('default value')) >>> def_dict['missing'] 'DEFAULT VALUE'

      在上面的代碼中,您創建了一個名為factory().?該函數接受一個參數,進行一些處理,并返回最終結果。然后,您創建一個defaultdict并用于lambda將字符串傳遞'default value'給factory().?當您嘗試訪問丟失的密鑰時,將運行以下步驟:

      字典def_dict調用 its?.default_factory,它保存對lambda函數的引用。

      該lambda函數被調用和返回值從調用的結果factory()與'default value'作為參數。

      如果您正在使用def_dict并且突然需要將參數更改為factory(),那么您可以執行以下操作:

      >>>

      >>> def_dict.default_factory = lambda: factory('another default value') >>> def_dict['another_missing'] 'ANOTHER DEFAULT VALUE'

      這一次,factory()接受一個新的字符串參數 (?'another default value')。從現在開始,如果您嘗試訪問或修改丟失的鍵,那么您將獲得一個新的默認值,即 string?'ANOTHER DEFAULT VALUE'。

      最后,您可能會遇到需要不同于0或的默認值的情況[]。在這種情況下,你也可以使用lambda,以產生不同的默認值。例如,假設您有一個list整數,您需要計算每個數字的累積乘積。然后,您可以使用 a?defaultdictwith?lambda,如下所示:

      >>>

      >>> from collections import defaultdict >>> lst = [1, 1, 2, 1, 2, 2, 3, 4, 3, 3, 4, 4] >>> def_dict = defaultdict(lambda: 1) >>> for number in lst: ... def_dict[number] *= number ... >>> def_dict defaultdict( at 0x...70>, {1: 1, 2: 8, 3: 27, 4: 64})

      在這里,您lambda用來提供默認值1。使用這個初始值,您可以計算 中每個數字的累積乘積lst。請注意,您無法使用 using 獲得相同的結果,int因為返回的默認值int始終為0,這對于您需要在此處執行的乘法運算來說不是一個好的初始值。

      使用?functools.partial()

      functools.partial(func, *args, **keywords)是一個返回partial對象的函數。當您使用位置參數 (?args) 和關鍵字參數 (?keywords)調用此對象時,它的行為與您調用func(*args, **keywords).?您可以利用 的這種行為partial()并使用它.default_factory在 Python 中傳遞參數defaultdict。下面是一個例子:

      >>>

      >>> def factory(arg): ... # Do some processing here... ... result = arg.upper() ... return result ... >>> from functools import partial >>> def_dict = defaultdict(partial(factory, 'default value')) >>> def_dict['missing'] 'DEFAULT VALUE' >>> def_dict.default_factory = partial(factory, 'another default value') >>> def_dict['another_missing'] 'ANOTHER DEFAULT VALUE'

      在這里,您創建了一個 Pythondefaultdict并用于partial()為.default_factory.?請注意,您還可以更新.default_factory以使用 callable 的另一個參數factory()。這種行為可以為您的defaultdict對象增加很多靈活性。

      結論

      Pythondefaultdict類型是 Python 標準庫在名為collections.?該類繼承自dict,其主要附加功能是為缺失的鍵提供默認值。在本教程中,您學習了如何使用 Pythondefaultdict類型來處理字典中缺失的鍵。

      您現在可以:

      創建并使用Pythondefaultdict來處理丟失的鍵

      解決與分組、計數和累加操作相關的實際問題

      了解defaultdict和之間的實現差異dict

      決定何時以及為何使用 Pythondefaultdict而不是標準dict

      Pythondefaultdict類型是一種方便且高效的數據結構,旨在幫助您處理字典中缺少的鍵。試一試,讓你的代碼更快、更易讀、更 Pythonic!

      Python

      版權聲明:本文內容由網絡用戶投稿,版權歸原作者所有,本站不擁有其著作權,亦不承擔相應法律責任。如果您發現本站中有涉嫌抄襲或描述失實的內容,請聯系我們jiasou666@gmail.com 處理,核實后本網站將在24小時內刪除侵權內容。

      上一篇:excel表格批量修改數據格式的方法(excel表格如何批量修改格式)
      下一篇:車聯網產業發展報告(2019)(車聯網產業發展報告)
      相關文章
      亚洲欧洲日韩综合| 亚洲成亚洲乱码一二三四区软件| 国产亚洲精品国产| 亚洲精品国精品久久99热一| 91麻豆精品国产自产在线观看亚洲| 亚洲国产人成中文幕一级二级| 日韩亚洲翔田千里在线| 色偷偷亚洲第一综合| 亚洲av综合日韩| 亚洲成a人片在线观看日本麻豆| 亚洲av成人一区二区三区在线观看| 少妇亚洲免费精品| 亚洲成a人片在线观看老师| 亚洲美女在线国产| 亚洲综合日韩久久成人AV| 亚洲中文久久精品无码| 亚洲精品白浆高清久久久久久| 久久被窝电影亚洲爽爽爽| 亚洲av永久无码精品古装片| 亚洲第一福利视频| 99久久亚洲综合精品成人网| 亚洲色欲www综合网| 亚洲六月丁香六月婷婷色伊人| 亚洲不卡1卡2卡三卡2021麻豆| 亚洲精品中文字幕无乱码麻豆| 中文字幕乱码亚洲无线三区| 亚洲熟妇久久精品| 在线观看亚洲免费| 久久精品亚洲福利| 亚洲a一级免费视频| 中文字幕亚洲第一在线| 亚洲日本在线播放| 亚洲美国产亚洲AV| 亚洲av再在线观看 | 亚洲最大激情中文字幕| 久久夜色精品国产亚洲| 亚洲精品高清视频| 亚洲一级片在线观看| 亚洲熟妇AV日韩熟妇在线| 国产精品手机在线亚洲| 亚洲综合日韩久久成人AV|