Python入門:ChainMap 有效管理多個上下文


摘要: Python的ChainMap從collections模塊提供用於管理多個詞典作為單個的有效工具。

本文分享自華為雲社區《從零開始學python | ChainMap 有效管理多個上下文》,作者: Yuchuan 。

有時,當您使用多個不同的詞典時,您需要將它們作為一個進行分組和管理。在其他情況下,您可以擁有多個代表不同范圍或上下文的字典,並且需要將它們作為單個字典來處理,以便您可以按照給定的順序或優先級訪問底層數據。在這種情況下,你可以利用Python的的ChainMap從collections模塊。

ChainMap將多個字典和映射組合在一個具有字典式行為的可更新視圖中。此外,ChainMap還提供了允許您有效管理各種詞典、定義關鍵查找優先級等的功能。

在本教程中,您將學習如何:

  • 在 Python 程序中創建ChainMap實例
  • 探索差異之間ChainMap和dict
  • 用於ChainMap將多個字典合二為一
  • 管理鍵查找優先使用ChainMap

為了充分利用本教程,您應該了解在 Python 中使用字典和列表的基礎知識。

在旅程結束時,您將找到一些實際示例,這些示例將幫助您更好地了解ChainMap.

Python 入門 ChainMap

Python的ChainMap加入collections中的Python 3.3作為管理多一個方便的工具范圍環境。此類允許您將多個字典和其他映射組合在一起,使它們在邏輯上顯示並表現為一個整體。它創建一個單一的可更新視圖,其工作方式類似於常規字典,但有一些內部差異。

ChainMap不會將其映射合並在一起。相反,它將它們保存在一個內部映射列表中。然后ChainMap在該列表的頂部重新實現常見的字典操作。由於內部列表保存對原始輸入映射的引用,因此這些映射中的任何更改都會影響整個ChainMap對象。

將輸入映射存儲在列表中允許您在給定的鏈映射中擁有重復的鍵。如果您執行鍵查找,則ChainMap搜索映射列表,直到找到第一次出現的目標鍵。如果密鑰丟失,那么您會KeyError像往常一樣得到一個。

當您需要管理嵌套作用域時,將映射存儲在列表中會真正發揮作用,其中每個映射代表一個特定的作用域或上下文。

為了更好地理解作用域和上下文的含義,請考慮 Python 如何解析名稱。當 Python 查找名稱時,它會在locals()、globals()、 中搜索,最后builtins直到找到第一次出現的目標名稱。如果名稱不存在,那么您會得到一個NameError. 處理范圍和上下文是您可以解決的最常見的問題ChainMap。

當您使用 時ChainMap,您可以使用不相交或相交的鍵鏈接多個字典。

在第一種情況下,ChainMap允許您將所有字典視為一個。因此,您可以像使用單個字典一樣訪問鍵值對。在第二種情況下,除了將字典作為一個來管理之外,您還可以利用內部映射列表為字典中的重復鍵定義某種訪問優先級。這就是為什么ChainMap對象非常適合處理多個上下文。

一個奇怪的行為ChainMap是突變,例如更新、添加、刪除、清除和彈出鍵,僅作用於內部映射列表中的第一個映射。以下是主要功能的摘要ChainMap:

  • 從多個輸入映射構建可更新視圖
  • 提供與字典幾乎相同的界面,但具有一些額外的功能
  • 不合並輸入映射,而是將它們保存在內部公共列表中
  • 查看輸入映射的外部變化
  • 可以包含具有不同值的重復鍵
  • 通過內部映射列表按順序搜索鍵
  • 在搜索整個映射列表后KeyError缺少鍵時拋出 a
  • 僅對內部列表中的第一個映射執行更改

在本教程中,您將詳細了解ChainMap. 以下部分將指導您了解如何ChainMap在您的代碼中創建新實例。

實例化 ChainMap

要ChainMap在您的 Python 代碼中創建,您首先需要從該類導入collections,然后像往常一樣調用它。類初始值設定項可以將零個或多個映射作為參數。沒有參數,它初始化一個鏈映射,里面有一個空字典:

>>> from collections import ChainMap
>>> from collections import OrderedDict, defaultdict

>>> # Use no arguments
>>> ChainMap()
ChainMap({})

>>> # Use regular dictionaries
>>> numbers = {"one": 1, "two": 2}
>>> letters = {"a": "A", "b": "B"}

>>> ChainMap(numbers, letters)
ChainMap({'one': 1, 'two': 2}, {'a': 'A', 'b': 'B'})

>>> ChainMap(numbers, {"a": "A", "b": "B"})
ChainMap({'one': 1, 'two': 2}, {'a': 'A', 'b': 'B'})

>>> # Use other mappings
>>> numbers = OrderedDict(one=1, two=2)
>>> letters = defaultdict(str, {"a": "A", "b": "B"})
>>> ChainMap(numbers, letters)
ChainMap(
    OrderedDict([('one', 1), ('two', 2)]),
    defaultdict(<class 'str'>, {'a': 'A', 'b': 'B'})
)

在這里,您ChainMap使用不同的映射組合創建多個對象。在每種情況下,ChainMap返回所有輸入映射的單個類似字典的視圖。請注意,您可以使用任何類型的映射,例如OrderedDict和defaultdict。

您還可以ChainMap使用類方法 創建對象.fromkeys()。此方法可以采用可迭代的鍵和所有鍵的可選默認值:

>>> from collections import ChainMap

>>> ChainMap.fromkeys(["one", "two","three"])
ChainMap({'one': None, 'two': None, 'three': None})

>>> ChainMap.fromkeys(["one", "two","three"], 0)
ChainMap({'one': 0, 'two': 0, 'three': 0})

如果你調用.fromkeys()上ChainMap與迭代鍵作為參數,那么你得到的鏈條地圖一個字典。鍵來自輸入可迭代對象,值默認為None。或者,您可以傳遞第二個參數 來.fromkeys()為每個鍵提供合理的默認值。

運行類似字典的操作

ChainMap支持與常規字典相同的 API,用於訪問現有密鑰。擁有ChainMap對象后,您可以使用字典樣式的鍵查找來檢索現有鍵,或者您可以使用.get():

>>> from collections import ChainMap

>>> numbers = {"one": 1, "two": 2}
>>> letters = {"a": "A", "b": "B"}

>>> alpha_num = ChainMap(numbers, letters)
>>> alpha_num["two"]
2

>>> alpha_num.get("a")
'A'

>>> alpha_num["three"]
Traceback (most recent call last):
    ...
KeyError: 'three'

鍵查找搜索目標鏈映射中的所有映射,直到找到所需的鍵。如果密鑰不存在,那么您將獲得通常的KeyError. 現在,當您有重復的鍵時,查找操作的行為如何?在這種情況下,您將獲得第一次出現的目標鍵:

>>> from collections import ChainMap

>>> for_adoption = {"dogs": 10, "cats": 7, "pythons": 3}
>>> vet_treatment = {"dogs": 4, "cats": 3, "turtles": 1}
>>> pets = ChainMap(for_adoption, vet_treatment)

>>> pets["dogs"]
10
>>> pets.get("cats")
7
>>> pets["turtles"]
1

當您訪問重復鍵(例如"dogs"and )時"cats",鏈映射僅返回該鍵的第一次出現。在內部,查找操作按照它們在內部映射列表中出現的相同順序搜索輸入映射,這也是您將它們傳遞到類的初始值設定項的確切順序。

這種一般行為也適用於迭代:

>>> from collections import ChainMap

>>> for_adoption = {"dogs": 10, "cats": 7, "pythons": 3}
>>> vet_treatment = {"dogs": 4, "cats": 3, "turtles": 1}
>>> pets = ChainMap(for_adoption, vet_treatment)

>>> for key, value in pets.items():
...     print(key, "->", value)
...
dogs -> 10
cats -> 7
turtles -> 1
pythons -> 3

該for循環遍歷在字典pets和打印每個鍵-值對的第一次出現。您還可以直接或使用and遍歷字典.keys(),.values()就像使用任何字典一樣:

>>> from collections import ChainMap

>>> for_adoption = {"dogs": 10, "cats": 7, "pythons": 3}
>>> vet_treatment = {"dogs": 4, "cats": 3, "turtles": 1}
>>> pets = ChainMap(for_adoption, vet_treatment)

>>> for key in pets:
...     print(key, "->", pets[key])
...
dogs -> 10
cats -> 7
turtles -> 1
pythons -> 3

>>> for key in pets.keys():
...     print(key, "->", pets[key])
...
dogs -> 10
cats -> 7
turtles -> 1
pythons -> 3

>>> for value in pets.values():
...     print(value)
...
10
7
1
3

同樣,行為是相同的。每次迭代都經過底層鏈映射中每個鍵、項和值的第一次出現。

ChainMap還支持突變。換句話說,它允許您更新、添加、刪除和彈出鍵值對。這種情況下的不同之處在於這些操作僅作用於第一個映射:

>>> from collections import ChainMap

>>> numbers = {"one": 1, "two": 2}
>>> letters = {"a": "A", "b": "B"}

>>> alpha_num = ChainMap(numbers, letters)
>>> alpha_num
ChainMap({'one': 1, 'two': 2}, {'a': 'A', 'b': 'B'})

>>> # Add a new key-value pair
>>> alpha_num["c"] = "C"
>>> alpha_num
ChainMap({'one': 1, 'two': 2, 'c': 'C'}, {'a': 'A', 'b': 'B'})

>>> # Update an existing key
>>> alpha_num["b"] = "b"
>>> alpha_num
ChainMap({'one': 1, 'two': 2, 'c': 'C', 'b': 'b'}, {'a': 'A', 'b': 'B'})

>>> # Pop keys
>>> alpha_num.pop("two")
2
>>> alpha_num.pop("a")
Traceback (most recent call last):
    ...
KeyError: "Key not found in the first mapping: 'a'"

>>> # Delete keys
>>> del alpha_num["c"]
>>> alpha_num
ChainMap({'one': 1, 'b': 'b'}, {'a': 'A', 'b': 'B'})
>>> del alpha_num["a"]
Traceback (most recent call last):
    ...
KeyError: "Key not found in the first mapping: 'a'"

>>> # Clear the dictionary
>>> alpha_num.clear()
>>> alpha_num
ChainMap({}, {'a': 'A', 'b': 'B'})

改變給定鏈映射內容的操作只會影響第一個映射,即使您嘗試改變的鍵存在於列表中的其他映射中。例如,當您嘗試"b"在第二個映射中進行更新時,真正發生的是您向第一個字典添加了一個新鍵。

您可以利用此行為來創建不修改原始輸入字典的可更新鏈映射。在這種情況下,您可以使用空字典作為第一個參數ChainMap:

>>> from collections import ChainMap

>>> numbers = {"one": 1, "two": 2}
>>> letters = {"a": "A", "b": "B"}

>>> alpha_num = ChainMap({}, numbers, letters)
>>> alpha_num
ChainMap({}, {'one': 1, 'two': 2}, {'a': 'A', 'b': 'B'})

>>> alpha_num["comma"] = ","
>>> alpha_num["period"] = "."

>>> alpha_num
ChainMap(
    {'comma': ',', 'period': '.'},
    {'one': 1, 'two': 2},
    {'a': 'A', 'b': 'B'}
)

在這里,您使用一個空字典 ( {}) 創建alpha_num. 這確保您執行的更改alpha_num永遠不會影響您的兩個原始輸入字典numbers和letters,並且只會影響列表開頭的空字典。

合並與鏈接字典

作為使用 鏈接多個字典的替代方法ChainMap,您可以考慮使用dict.update()以下方法將它們合並在一起:

>>> from collections import ChainMap

>>> # Chain dictionaries with ChainMap
>>> for_adoption = {"dogs": 10, "cats": 7, "pythons": 3}
>>> vet_treatment = {"hamsters": 2, "turtles": 1}

>>> ChainMap(for_adoption, vet_treatment)
ChainMap(
    {'dogs': 10, 'cats': 7, 'pythons': 3},
    {'hamsters': 2, 'turtles': 1}
)

>>> # Merge dictionaries with .update()
>>> pets = {}
>>> pets.update(for_adoption)
>>> pets.update(vet_treatment)
>>> pets
{'dogs': 10, 'cats': 7, 'pythons': 3, 'hamsters': 2, 'turtles': 1}

在此特定示例中,當您從兩個具有唯一鍵的現有字典構建鏈映射和等效字典時,您會得到類似的結果。

與將字典.update()與ChainMap. 第一個也是最重要的缺點是,您無法使用多個范圍或上下文來管理和確定對重復鍵的訪問的優先級。使用.update(),您為給定鍵提供的最后一個值將始終占上風:

>>> for_adoption = {"dogs": 10, "cats": 7, "pythons": 3}
>>> vet_treatment = {"cats": 2, "dogs": 1}

>>> # Merge dictionaries with .update()
>>> pets = {}
>>> pets.update(for_adoption)
>>> pets.update(vet_treatment)
>>> pets
{'dogs': 1, 'cats': 2, 'pythons': 3}

常規詞典不能存儲重復的鍵。每次調用.update()現有鍵的值時,該鍵都會更新為新值。在這種情況下,您將無法使用不同的范圍對重復密鑰的訪問進行優先級排序。

注意:從Python 3.5 開始,您還可以使用字典解包運算符 ( **)將字典合並在一起。此外,如果您使用的是Python 3.9,那么您可以使用字典聯合運算符 ( |) 將兩個字典合並為一個新字典。

現在假設您有n 個不同的映射,每個映射最多有m 個鍵。ChainMap從它們創建對象將花費O ( n )執行時間,而在最壞的情況下檢索鍵將花費O ( n ),其中目標鍵位於內部映射列表的最后一個字典中。

或者,.update()在循環中創建一個常規字典需要O ( nm ),而從最終字典中檢索一個鍵需要O (1)。

結論是,如果您經常創建字典鏈並且每次只執行幾個鍵查找,那么您應該使用ChainMap. 如果是相反的方式,則使用常規詞典,除非您需要重復的鍵或多個范圍。

合並字典和鏈接字典之間的另一個區別是,當您使用 時ChainMap,輸入字典中的外部更改會影響基礎鏈,而合並字典的情況並非如此。

探索附加功能 ChainMap

ChainMap提供與常規 Python 字典大致相同的 API 和功能,但有一些您已經知道的細微差別。ChainMap還支持一些特定於其設計和目標的附加功能。

在本節中,您將了解所有這些附加功能。當您訪問字典中的鍵值對時,您將了解它們如何幫助您管理不同的范圍和上下文。

管理映射列表 .maps

ChainMap將所有輸入映射存儲在一個內部列表中。此列表可通過名為的公共實例屬性訪問.maps,並且可由用戶更新。中的映射.maps順序與您將它們傳遞到 中的順序相匹配ChainMap。此順序定義執行鍵查找操作時的搜索順序。

以下是您如何訪問的示例.maps:

>>> from collections import ChainMap

>>> for_adoption = {"dogs": 10, "cats": 7, "pythons": 3}
>>> vet_treatment = {"dogs": 4, "turtles": 1}

>>> pets = ChainMap(for_adoption, vet_treatment)
>>> pets.maps
[{'dogs': 10, 'cats': 7, 'pythons': 3}, {'dogs': 4, 'turtles': 1}]

在這里,您用於.maps訪問pets保存的映射的內部列表。此列表是常規 Python 列表,因此您可以手動添加和刪除映射、遍歷列表、更改映射的順序等:

>>> from collections import ChainMap

>>> for_adoption = {"dogs": 10, "cats": 7, "pythons": 3}
>>> vet_treatment = {"cats": 1}
>>> pets = ChainMap(for_adoption, vet_treatment)

>>> pets.maps.append({"hamsters": 2})
>>> pets.maps
[{'dogs': 10, 'cats': 7, 'pythons': 3}, {"cats": 1}, {'hamsters': 2}]

>>> del pets.maps[1]
>>> pets.maps
[{'dogs': 10, 'cats': 7, 'pythons': 3}, {'hamsters': 2}]

>>> for mapping in pets.maps:
...     print(mapping)
...
{'dogs': 10, 'cats': 7, 'pythons': 3}
{'hamsters': 2}

在這些示例中,您首先將一個新字典添加到.mapsusing 中.append()。然后使用del 關鍵字刪除位置 處的字典1。您可以.maps像管理任何常規 Python 列表一樣進行管理。

注意:映射的內部列表.maps將始終包含至少一個映射。例如,如果您使用ChainMap()不帶參數創建一個空鏈映射,那么該列表將存儲一個空字典。

您可以.maps在對所有映射執行操作時對其進行迭代。遍歷映射列表的可能性允許您對每個映射執行不同的操作。使用此選項,您可以解決僅更改列表中的第一個映射的默認行為。

一個有趣的例子是,您可以使用以下命令顛倒當前映射列表的順序.reverse():

>>> from collections import ChainMap

>>> for_adoption = {"dogs": 10, "cats": 7, "pythons": 3}
>>> vet_treatment = {"cats": 1}
>>> pets = ChainMap(for_adoption, vet_treatment)
>>> pets
ChainMap({'dogs': 10, 'cats': 7, 'pythons': 3}, {'cats': 1})

>>> pets.maps.reverse()
>>> pets
ChainMap({'cats': 1}, {'dogs': 10, 'cats': 7, 'pythons': 3})

反轉內部映射列表允許您在查找鏈映射中的給定鍵時反轉搜索順序。現在,當您查找 時"cats",您會得到接受獸醫治療的貓的數量,而不是准備收養的貓的數量。

添加新的子上下文 .new_child()

ChainMap也實現.new_child(). 此方法可選擇將映射作為參數並返回一個新ChainMap實例,該實例包含輸入映射,后跟底層鏈映射中的所有當前映射:

>>> from collections import ChainMap

>>> mom = {"name": "Jane", "age": 31}
>>> dad = {"name": "John", "age": 35}

>>> family = ChainMap(mom, dad)
>>> family
ChainMap({'name': 'Jane', 'age': 31}, {'name': 'John', 'age': 35})

>>> son = {"name": "Mike", "age": 0}
>>> family = family.new_child(son)

>>> for person in family.maps:
...     print(person)
...
{'name': 'Mike', 'age': 0}
{'name': 'Jane', 'age': 31}
{'name': 'John', 'age': 35}

在這里,.new_child()返回一個ChainMap包含新映射的新對象son,后跟舊映射,mom和dad。請注意,新映射現在占據內部映射列表中的第一個位置,.maps。

使用.new_child(),您可以創建一個子上下文,您可以在不更改任何現有映射的情況下更新該子上下文。例如,如果您.new_child()不帶參數調用,則它使用一個空字典並將其放在.maps. 在此之后,您可以對新的空映射執行任何更改,使映射的其余部分保持只讀。

跳過子上下文 .parents

的另一個有趣的功能ChainMap是.parents。此屬性返回一個新ChainMap實例,其中包含底層鏈映射中除第一個之外的所有映射。當您在給定鏈映射中搜索鍵時,此功能對於跳過第一個映射很有用:

>>> from collections import ChainMap

>>> mom = {"name": "Jane", "age": 31}
>>> dad = {"name": "John", "age": 35}
>>> son = {"name": "Mike", "age":  0}

>>> family = ChainMap(son, mom, dad)
>>> family
ChainMap(
    {'name': 'Mike', 'age': 0},
    {'name': 'Jane', 'age': 31},
    {'name': 'John', 'age': 35}
)

>>> family.parents
ChainMap({'name': 'Jane', 'age': 31}, {'name': 'John', 'age': 35})

在此示例中,您使用.parents跳過包含兒子數據的第一個字典。在某種程度上,.parents是 的逆.new_child()。前者刪除一個字典,而后者在列表的開頭添加一個新字典。在這兩種情況下,您都會獲得一個新的鏈圖。

管理范圍和上下文 ChainMap

可以說,主要用例ChainMap是提供一種有效的方法來管理多個范圍或上下文並處理重復鍵的訪問優先級。當您有多個存儲重復鍵的字典並且您想定義代碼訪問它們的順序時,此功能很有用。

在ChainMap文檔 中,您將找到一個經典示例,該示例模擬 Python 如何解析不同命名空間中的變量名稱。

當 Python 查找名稱時,它會按照相同的順序依次搜索本地、全局和內置作用域,直到找到目標名稱。Python 作用域是將名稱映射到對象的字典。

要模擬 Python 的內部查找鏈,您可以使用鏈映射:

>>> import builtins

>>> # Shadow input with a global name
>>> input = 42

>>> pylookup = ChainMap(locals(), globals(), vars(builtins))

>>> # Retrieve input from the global namespace
>>> pylookup["input"]
42

>>> # Remove input from the global namespace
>>> del globals()["input"]

>>> # Retrieve input from the builtins namespace
>>> pylookup["input"]
<built-in function input>

在此示例中,您首先創建一個名為的全局變量input,該變量隱藏input()作用builtins域中的內置函數。然后創建pylookup一個鏈映射,其中包含保存每個 Python 作用域的三個字典。

當您input從檢索時pylookup,您會42從全局范圍中獲得值。如果您input從globals()字典中刪除鍵並再次訪問它,那么您input()將從builtins作用域中獲得內置函數,它在 Python 的查找鏈中具有最低優先級。

同樣,您可以使用ChainMap來定義和管理重復鍵的鍵查找順序。這允許您優先訪問所需的重復密鑰實例。

ChainMap在標准庫中跟蹤

的起源ChainMap密切相關的性能問題中ConfigParser,其生活中configparser的標准庫模塊。有了ChainMap,核心 Python 開發人員通過優化ConfigParser.get().

您還可以在模塊中找到ChainMap作為一部分。此類將字符串模板作為參數,並允許您執行PEP 292 中所述的字符串替換。輸入字符串模板包含您以后可以用實際值替換的嵌入標識符:Templatestring

>>> import string

>>> greeting = "Hey $name, welcome to $place!"
>>> template = string.Template(greeting)

>>> template.substitute({"name": "Jane", "place": "the World"})
'Hey Jane, welcome to the World!'

當您為字典提供值name並place通過字典提供值時,.substitute()在模板字符串中替換它們。此外,.substitute()可以將值作為關鍵字參數 ( **kwargs),這在某些情況下可能會導致名稱沖突:

>>> import string

>>> greeting = "Hey $name, welcome to $place!"
>>> template = string.Template(greeting)

>>> template.substitute(
...     {"name": "Jane", "place": "the World"},
...     place="Real Python"
... )
'Hey Jane, welcome to Real Python!'

在此示例中,.substitute()替換place為您作為關鍵字參數提供的值,而不是輸入字典中的值。如果您深入研究此方法的代碼,就會發現它用於ChainMap在發生名稱沖突時有效地管理輸入值的優先級。

這是來自 的源代碼片段.substitute():

# string.py
# Snip...
from collections import ChainMap as _ChainMap

_sentinel_dict = {}

class Template:
    """A string class for supporting $-substitutions."""
    # Snip...

    def substitute(self, mapping=_sentinel_dict, /, **kws):
        if mapping is _sentinel_dict:
            mapping = kws
        elif kws:
            mapping = _ChainMap(kws, mapping)
        # Snip...

在這里,突出顯示的行發揮了作用。它使用一個鏈映射,該映射將兩個字典kws和mapping, 作為參數。通過放置kws作為第一個參數,該方法設置輸入數據中重復標識符的優先級。

將 PythonChainMap付諸行動

到目前為止,您已經學會了如何將ChainMap多個字典合二為一。您還了解了ChainMap本課程與常規詞典的特點和不同之處。的用例ChainMap是相當具體的。它們包括:

  • 單個視圖中有效地對多個字典進行分組
  • 搜索具有特定優先級的多個詞典
  • 提供一系列默認值並管理它們的優先級
  • 提高經常計算字典子集的代碼的性能

在本節中,您將編寫一些實際示例,這些示例將幫助您更好地了解如何使用它ChainMap來解決實際問題。

將多個庫存作為一個訪問

您將編寫代碼的第一個示例用於ChainMap在單個視圖中高效搜索多個字典。在這種情況下,您會假設您有一堆具有唯一鍵的獨立字典。

假設您正在經營一家銷售水果和蔬菜的商店。您已經編寫了一個 Python 應用程序來管理您的庫存。該應用程序從數據庫中讀取數據並返回兩個字典,分別包含有關水果和蔬菜價格的數據。您需要一種有效的方法在單個字典中對這些數據進行分組和管理。

經過一些研究,您最終使用ChainMap:

>>> from collections import ChainMap

>>> fruits_prices = {"apple": 0.80, "grape": 0.40, "orange": 0.50}
>>> veggies_prices = {"tomato": 1.20, "pepper": 1.30, "onion": 1.25}
>>> prices = ChainMap(fruits_prices, veggies_prices)

>>> order = {"apple": 4, "tomato": 8, "orange": 4}

>>> for product, units in order.items():
...     price = prices[product]
...     subtotal = units * price
...     print(f"{product:6}: ${price:.2f} × {units} = ${subtotal:.2f}")
...
apple : $0.80 × 4 = $3.20
tomato: $1.20 × 8 = $9.60
orange: $0.50 × 4 = $2.00

在此示例中,您使用 aChainMap創建一個類似字典的對象,該對象將來自fruits_prices和 的數據分組veggies_prices。這允許您訪問底層數據,就像您有效地擁有單個字典一樣。該for循環迭代的產品在一個給定的order。然后它計算每種產品類型的小計,並將其打印在您的屏幕上。

您可能會考慮將數據分組到一個新字典中,.update()在循環中使用。當您的產品種類有限且庫存較少時,這可能會很好地工作。但是,如果您管理許多不同類型的產品,那么.update()與ChainMap.

使用ChainMap來解決這類問題也可以幫助你定義不同批次產品的優先級,讓你管理一個先入/先出(你的庫存FIFO)的方式。

優先考慮命令行應用程序設置

ChainMap在管理應用程序中的默認配置值方面特別有用。如您所知,它的主要功能之一ChainMap是允許您設置關鍵查找操作的優先級。這聽起來像是解決管理應用程序配置問題的正確工具。

例如,假設您正在開發一個命令行界面 (CLI)應用程序。該應用程序允許用戶指定用於連接到 Internet 的代理服務。設置優先級是:

  1. 命令行選項 ( --proxy, -p)
  2. 用戶主目錄中的本地配置文件
  3. 系統范圍的代理配置

如果用戶在命令行提供代理,則應用程序必須使用該代理。否則,應用程序應使用下一個配置對象中提供的代理,依此類推。這是最常見的用例之一ChainMap。在這種情況下,您可以執行以下操作:

>>> from collections import ChainMap

>>> cmd_proxy = {}  # The user doesn't provide a proxy
>>> local_proxy = {"proxy": "proxy.local.com"}
>>> system_proxy = {"proxy": "proxy.global.com"}

>>> config = ChainMap(cmd_proxy, local_proxy, system_proxy)
>>> config["proxy"]
'proxy.local.com'

ChainMap允許您為應用程序的代理配置定義適當的優先級。鍵查找搜索cmd_proxy,然后local_proxy,最后system_proxy,返回手頭鍵的第一個實例。在示例中,用戶沒有在命令行提供代理,因此應用程序從 中獲取代理local_proxy,這是列表中的下一個設置提供程序。

管理默認參數值

的另一個用例ChainMap是管理方法和函數中的默認參數值。假設您正在編寫一個應用程序來管理有關貴公司員工的數據。您有以下類,它代表一個通用用戶:

class User:
    def __init__(self, name, user_id, role):
        self.name = name
        self.user_id = user_id
        self.role = role

    # Snip...

在某些時候,您需要添加一項功能,允許員工訪問CRM系統的不同組件。您的第一個想法是修改User以添加新功能。但是,這可能會使類過於復雜,因此您決定創建一個子類CRMUser,以提供所需的功能。

該類將用戶name和 CRMcomponent作為參數。它也需要一些**kwargs。您希望以CRMUser一種允許您為基類的初始值設定項提供合理的默認值的方式實現,而不會失去**kwargs.

以下是您使用以下方法解決問題的方法ChainMap:

from collections import ChainMap

class CRMUser(User):
    def __init__(self, name, component, **kwargs):
        defaults = {"user_id": next(component.user_id), "role": "read"}
        super().__init__(name, **ChainMap(kwargs, defaults))

在此代碼示例中,您將創建User. 在類初始化,你拿name,component以及**kwargs作為參數。然后,您創建一個本地字典,其中user_id和具有默認值role。然后.__init__()使用super(). 在此調用中,您name直接傳遞給父級的初始值設定項,並使用鏈映射為其余參數提供默認值。

請注意,該ChainMap對象將kwargs然后defaults作為參數。此順序可確保在實例化類時手動提供的參數 ( kwargs) 優先於defaults值。

結論

Python的ChainMap從collections模塊提供用於管理多個詞典作為單個的有效工具。當您有多個字典表示不同的范圍或上下文並且需要設置對底層數據的訪問優先級時,這個類很方便。

ChainMap將多個字典和映射組合在一個可更新的視圖中,該視圖的工作方式與字典非常相似。您可以使用ChainMap對象在 Python 中高效地處理多個字典、定義關鍵查找優先級以及管理多個上下文。

在本教程中,您學習了如何:

  • 在 Python 程序中創建ChainMap實例
  • 探索差異之間ChainMap和dict
  • 使用多個字典作為一個管理ChainMap
  • 設置關鍵查找操作的優先級ChainMap

在本教程中,您還編寫了一些實際示例,幫助您更好地理解何時以及如何ChainMap在 Python 代碼中使用。

 

點擊關注,第一時間了解華為雲新鮮技術~


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM