关于python:Python入门ChainMap-有效管理多个上下文

2次阅读

共计 14918 个字符,预计需要花费 38 分钟才能阅读完成。

摘要: 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 代码中应用。

点击关注,第一工夫理解华为云陈腐技术~

正文完
 0