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 代码中使用。

点击关注,第一时间了解华为云新鲜技术~

相关推荐

鸿蒙案例上新啦 | 购物、听歌、健身:15个模板带你一键激活社交娱乐

页面层级如何组织、跨页共享状态如何同步、列表与详情及个人中心如何形成稳定闭环。鸿蒙开发实践中心将这些高频消费与互动场景沉淀为可参考的标准模板,提供从业务架构到界面实现的快捷实践入口。更多实战案例欢迎前往鸿蒙开发实践中心案例中心查看并开启云端体验。

华为云官方博客 364

鸿蒙案例上新啦 | 出门打车、到家上课:25个模板带你一屏打通日常生活

如何合理拆分页面组件、如何建立单一可信的状态源、如何让用户在每一步操作中都获得明确反馈。鸿蒙开发实践中心以标准化的应用与元服务模板为入口,将清晰的业务架构与可复用的代码结构直观呈现。不妨立即挑选一个感兴趣的方向进入云端环境,从页面布局、组件封装、状态管理到路由组织,亲手跑通你的鸿蒙实战项目。

华为云官方博客 343

LakeAgent:DWS数据仓库的第二大脑,告别 “翻译式运维”,迈入智能自治时代

和普通 SQL 改写工具不同,LakeAgent 自动做多方案代价对比,主动规避负优化陷阱:补充分区键过滤实现剪枝,扫描分区由 37 收敛至 1,执行代价从 758.28 降至 113.74;未来的数据仓库,将不止能算、能存,更能看懂自己、诊断自己、理解自己。长久以来,数仓的智能都依附于人:信号需要人解读,瓶颈需要人研判,经验困在个体里,无法随底座永续生长。CPU 高、CPU 告警、CPU 诊断、CPU 使用率过高、CPU 满。语法、错误码、计费、性能调优、最佳实践、故障处置等全场景内容。

华为云官方博客 567

如何使用RDS Supabase后端BaaS能力:文件存储(File Storage)

• product-images(公开桶,允许公共读取):策略名称"Public read for product-images",允许SELECT,目标角色anon,策略定义bucket_id = 'product-images'。上传/删除需另配INSERT/DELETE策略,详见SQL。• product-specs(私有桶,仅认证用户访问自己的文件夹):建议拆为上传、读取、删除三条策略,策略定义均为 bucket_id = 'product-specs' 且仅限本人文件夹(详见下文 SQL)。

华为云官方博客 491

如何使用RDS Supabase后端BaaS能力:实时订阅与推送(Realtime)

Realtime 把数据库的每次变更同步到客户端:表里增、删、改一行,订阅者无需刷新页面或主动请求即可秒级收到。三大能力各司其职:• Postgres Changes — 监听表的 INSERT/UPDATE/DELETE,变化即通知• Broadcast — 客户端之间直接互发,适合瞬时通知、协同编辑• Presence — 跟踪频道里“谁在线”典型场景有实时数据展示(订单、库存、监控指标即时刷新)、即时通讯(消息收发、在线名单、“对方正在输入”)、协同编辑与实时通知。

华为云官方博客 484

AI Agent如何通过MCP连接到华为云RDS Supabase应用平台

Schema元数据列出所有可访问的表(按schema分组)。Schema元数据列出表的列及元数据(类型/默认值/约束)。Schema元数据列出所有约束(PK/FK/UNIQUE/CHECK/EXCLUDE)。可按schema/表/类型过滤。Schema元数据列出所有外键关系。可按schema/表过滤。Schema元数据列出所有索引及定义和大小。可按schema/表过滤。Schema元数据列出已应用的数据库迁移记录。Schema元数据列出已安装的PostgreSQL扩展。

华为云官方博客 484

华为大数据Flink让每一 CU 算力,跑出开源 5.74 倍的性能

1. 开源Apache Flink 已成为业界流计算事实标准,但在极端重负载场景下存在性能瓶颈。2. 华为大数据Flink通过多项自研深度优化,在执行计划、算子执行、状态存储三个层面构建协同的优化体系。3. Nexmark Benchmark 测试结果:整体性能提升 5.74 倍,极端重负载场景性能领先最高 18.3 倍。4. 100% 兼容 Apache Flink 1.20,业务代码零改造,无缝迁移。

华为云官方博客 388

LakeAgent实战 | 业务分析篇从一句提问到业务分析

本文以“订单金额变化”分析为例,展示LakeAgent如何通过自然语言提问,结合DataArtsStudio的数据资产与专业技能,帮助业务人员快速定位数据、生成SQL、追踪血缘、生成报告。从问题出发,逐步实现数据发现、变化洞察、原因追溯与团队协作,最终形成可复用的分析流程与企业规则,显著提升数据分析效率与一致性。

华为云官方博客 399

华为云RDS Supabase AI应用已开放公测 :30分钟搭建AI应用后端

认证、存储、实时推送、数据库API——4个后端模块,传统开发需要数周。今天,你只需要30分钟。AI创业公司的全栈开发工程师。上周老板说"下周要上线的AI对话应用,后端你来搞"——认证鉴权、数据库接口、文件存储、实时推送,每一个模块都需要数周搭建,而市场不会等你。小张打开华为云RDS Supabase,30分钟搭好认证、数据库API、对象存储,剩下的时间全用来打磨AI交互体验。基于开源Supabase+华为云RDS PostgreSQL全托管能力,为开发者提供。

华为云官方博客 407

如何使用RDS Supabase后端BaaS能力:数据库函数(Database Function)

上一篇介绍了 Database Data API——建表即自动生成增删改查接口,解决单表读写,但聚合统计、跨表事务、字段自动维护等逻辑已超出其能力边界。本篇推进到数据库函数(Database Function):把业务逻辑封装进数据库,前端通过 .rpc() 调用,由触发器自动响应数据变更。上一篇中,小张基于 Data API 完成了产品目录的建表与基础读写。但新需求随之而来:按分类统计产品数与价格区间、上架新品同时写产品表与定价表、产品修改后自动刷新更新时间。

华为云官方博客 471

如何使用RDS Supabase后端BaaS能力:创表即生成API(Database Data API)

认证、存储、实时推送、数据库API——4个后端模块,传统开发要数周。本系列逐个拆解,首篇聚焦数据库API(Database Data API):建表即得 API,无需手写后端。小张,AI创业公司的全栈工程师。老板让他做个产品目录页,展示产品与价格、支持上下架。传统做法要搭后端、写接口、配权限,工期少说1-2周;而在华为云RDS Supabase,建两张表、应用 SDK 直连,几分钟增删改查全跑通。

华为云官方博客 487

从一句话到一个能出去的链接,AI开发者空间做了什么

自行搭建的 Agent、正在使用的开源项目、团队既有的工具,都可以接入,可以通过AI创建或者手动搭建环境;华为云 AI开发者空间——面向开发者的云端一站式开发平台,做的就是把这段路接上:环境免安装,浏览器打开即为完整的 Linux 运行环境,预置的 AI 智能体开箱即用。第一个任务建议选能出结果的:做一个游戏、生成一个页面、创建一台实例,从需求走到可访问的链接,一次就能看清这个环境在做什么。沙箱是一个完整的 Linux 环境,依赖、服务、端口都是真实的,不是模拟。环境释放后,权限随之失效。

华为云官方博客 431

华为云码道配置自定义大模型,四种方式完整实操

案例介绍。

华为云官方博客 637

图数据库:实现穿透式监管的数据基座

穿透式监管标志着国企监管从“管企业”向“管资本”转型过程中的关键一跃。而图数据库正是这一跃的技术支点。从股权穿透到资金链穿透,从供应链穿透到数据穿透,华为GES图数据库正在将“穿透式监管”从理念变为可落地、可操作的实践。无论是天眼查式的全量链路查询,还是某集团财经的税务规则判断,抑或是芯片供应链式的部件风险分析,乃至通过Palantir构建决策知识图谱,其底层逻辑都是一致的——用图的力量,穿透一切形式的“隔离层”,直抵风险本质与责任核心。

华为云官方博客 671

《公共云三十问》正式收官,奔赴企业AI百场景新征程

今年6月底,公共云产业联盟重点推出《公共云三十问》专栏,以每周三问的节奏,连载十周,解读《先进公共云》白皮书,分享国计民生系列案例,旨在凝聚共识、推动落地实践。同时,专栏分析公共云在产业升级、民生福祉、科技创新、全球发展等方面的作用,梳理中国特色公共云发展路径:以普惠云拓展用云广度,以智算云提升用云深度,以自主云夯实用云厚度,以全球云打开用云跨度。这十问从专属云、行业云切入,深入政务、金融、交通、电力、钢铁、化工、油气、制造等国计民生场景,回答公共云“怎么用”。

华为云官方博客 474

开发者小技巧|IAM配置,让码道代码智能体连续30天免登录

案例介绍。

华为云官方博客 650

码道深度对接CodeArts,让需求自动落地为代码

案例介绍。

华为云官方博客 723

【公共云三十问 之三十】拥抱AI,企业如何建设面向智能体的专属云?

企业可分阶段建设覆盖开发、运行、运营及安全的一体化智能体平台——从构建一站式、高低码协同的智能体开发平台,到打造高性能、极致弹性的运行时环境,再到搭建AgentOps驱动的持续运营与运维体系,同时构建端到端、多层次的全链路安全防护体系。在数据湖建设方面,遵循“存量复用、平滑演进、AI导向、分步迭代”原则,分三阶段推进——打造AI多模数据湖核心能力、完成存算分离与统一元数据管理、实现闭环赋能全面支撑智能体规模化落地,在保护既有投资的前提下将传统数据平台转型为AI数据专属供给平台。

华为云官方博客 825

【公共云三十问 之二十九】国计民生之制造:制造云如何重塑数智时代的新质生产力?

平台打通软件交付的完整路径,覆盖需求管理、代码托管、代码检查、编译构建、制品仓库、部署、测试等关键环节,支持大模型能力融入研发全流程,实现从需求到交付的端到端管理。通过构建“研发一朵云”,方案打破地域与组织壁垒,实现跨团队协同开发,有效缩短软件交付周期,助力工业制造企业从“传统研发”走向“敏捷研发”,加速数智化转型进程。当制造云真正成为数据流通的平台、AI创新的底座,公共云的社会价值将从基础设施层向生产场景层持续延伸,为新型工业化建设和制造强国战略注入源源不断的动力,重塑数智时代的中国制造新质生产力。

华为云官方博客 792

告别重复手工迁仓,码道MCP+Skill实现仓库批量迁移

案例介绍。

华为云官方博客 791

【公共云三十问 之二十八】国计民生之油气:智算云如何叩响数智时代的“地心蓝金”?

西南油气田以“向地球深部要资源”为战略方向,联合以华为云Stack为代表的公共云服务商共同打造统一算力底座,建设西南区域数据湖与通用技术平台,构建“勘探开发工程技术、天然气生产过程管控、天然气产运储销”等三个一体化协同应用场景,支撑油气勘探、开发生产、管输储运等全业务链的数智化转型。当智算云真正成为数据流通的平台、AI创新的底座,油气田得以将算力效能、数据价值与智能技术深度融入勘探开发全链条,将“看不见的算力”转化为“摸得着的储量”,以智能化手段持续降低勘探风险、缩短发现周期、提高采收效率,确保生产安全。

华为云官方博客 613

【公共云三十问 之二十七】国计民生之化工:数智云如何重塑传统化工新范式?

在架构方面,方案构建云边协同的统一体系,涵盖AI协同、数据协同与运营协同三大维度——总部集中训练大模型,边缘节点负责推理执行,云边持续迭代,实现“边用边学”的智能闭环,模型平均精度提升20%以上。此外,方案提供一站式企业级工业物联网平台、数据湖与数据治理平台,覆盖工业数据分析“采、存、算、管、用”全流程,打通生产现场与管理系统间的数据壁垒,有效消除数据孤岛。层面,方案充分发挥软硬协同优势,实现计算、存储、网络与云平台的深度协同,训练线性度超95%,为大模型训练推理提供强劲算力支撑。▲化工“数智”新模式。

华为云官方博客 664

【公共云三十问 之二十六】国计民生之钢铁:数智云如何淬炼中国钢铁新力量?

同时,构建云边端协同的统一架构,支撑集团总部与各生产基地两级协同发展——总部集中训练大模型,边缘节点负责推理执行,云边持续迭代,实现训推一体化的资源调度与高效协同,为钢铁行业的数智化转型构建了坚实的算力底座。层面,方案构建覆盖“工业数采→边缘IT/OT融合→统一数据湖→数据治理平台→数据应用”全链路的一站式数据治理与流通平台,打通生产现场与管理系统间的数据壁垒,让数据“采得到、流得动、用得好”,加速数据价值从生产现场向经营决策层流动释放。湘潭钢铁集团的钢铁数智云,是钢铁工业智能云建设的标杆案例。

华为云官方博客 526

LakeWatch——MRS 智能运维Agent

从资源池、队列、作业等多个维度,对集群CPU与内存资源进行精细化分析与对比,直观呈现不同时间段的空闲、满载等使用状态,识别资源浪费,指导治理优化,为扩缩容决策提供数据支撑。对HDFS存储进行多维度深度分析(目录、用户、用户组等),涵盖使用量、增量、文件大小分布及数据热度等指标,帮助快速识别增长较快的目录、长期未访问的文件,统计目录文件数量与小文件占比,评估HDFS总存储量与扩缩容需求,为存储治理与资源规划提供全面数据支撑。Agent主动巡检,识别异常趋势并提前预警,让运维从"事后处理"转向"事前预防"。

华为云官方博客 515

华为云专题文档发布:丰富的AI案例、选型指导和概念解读

发布 “云服务选型”、“云计算概念”专题,提供云服务选型指导,助您选择匹配业务的云服务,解读云计算相关概念,开启云上学习之路。开发者工具和文档专区上线,提供从学习、实践到反馈的全链路支持。帮助中心新增100余篇AI案例。

华为云官方博客 286

一个人,也能用AI做点“星东西”

华为云星耀创想者大赛-2026华为云OPC创意大赛等你来~

华为云官方博客 291

从需求到可执行工作项:MCP+Skill 让智能体规划真正落地

回顾整个体验,以前让智能体帮我拆需求,它是洋洋洒洒给我列了一大篇 Markdown,看着挺像那么回事,但最后还是得我一条条往 CodeArts Req 里搬,搬完再手动挂父子关系、排日期,拆需求五分钟,搬运两小时。现在好了,MCP 给智能体接上了"手",Skill 给它装上了"项目经理的脑子",一句话下去,34 个工作项连带排期、层级关系全给我建好了,甘特图一拉,齐刷刷都在两周内,节省了多少工作量啊,美滋滋。本次实操在码道代码智能体基础上,构建了一套"需求→可执行工作项"的自动化规划方案。

华为云官方博客 578

华为云码道「码力续航计划」更新!

✅上新GLM‑5.3‑Flash,高速响应,代码生成、工程解析、Agent调试高效输出。新增GLM‑5.3‑Flash福利模型,每日解锁1000万免费Tokens。● 本次活动共发放总量价值100万元Tokens,发完即止!把耗算力的编码重活,交给华为云码道代码智能体🚀🚀。● 每日额度准时刷新,当日清零、次日满血重置。✅复杂算法、大项目解析、批量代码生成随便跑。✅不用抠额度、不用算消耗,放开调试大胆迭代。✅无需额外操作,登录即送,直接切换模型使用。● 把算力交给码道,让撸码不再内耗👏🏻。

华为云官方博客 692

【公共云三十问 之二十四】国计民生之交通:交通云如何铺就数智通衢新坦途?

交通行业作为支撑实体经济的关键基础设施,涵盖公路、铁路、机场、港口、地铁等众多子领域,其中,公路网络作为综合交通运输体系的基础骨架,承担着全国超过70%的客货运量,其数字化、智能化转型的需求尤为迫切。方面,提供Agent开发框架、知识库管理、RAG工程、意图识别、静态应用编排及模型评测等能力,支持客户将行业知识、业务流程与AI能力深度融合,快速构建面向收费稽核、路网调度、应急处置等场景的智能应用。开发并实现落地应用,全面覆盖建设、管理、养护、运营、服务、安全、办公七大业务领域。

华为云官方博客 464

【公共云三十问 之二十五】国计民生之电力:电力云如何点亮数智电力新引擎?

方案构建了覆盖数据获取、质量评估到标准化处理的全链路能力。基于公共云服务商智算底座及气象大模型,通过时空图神经网络重新建模产流机制,中国华电建成超50TB多源融合数据集,突破时空建模与机理融合等五大关键技术,短期径流预测精度较传统机理模型提升5个百分点以上,实现15天中期径流预测精度超85%,推理效率达秒级。基于气象大模型的基础架构与预训练能力,客户可直接在大模型之上开展增量训练与微调,充分利用场站所在区域的高精度历史数据(如3公里、1公里分辨率),针对不同场站和区域的地形地貌与气候特征进行模型调优。

华为云官方博客 381
上一篇: 高并发中,那些不得不说的线程池与ThreadPoolExecutor类
下一篇: 零代码以“王者荣耀”为例解析设计七原则
华为云开发者联盟
华为云开发者联盟 企业官方账号 企业官方账号
博客等级 码龄10年 72万+粉丝 5985原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值