diff --git a/cookbook/c01/p20_combine_map.py b/cookbook/c01/p20_combine_map.py new file mode 100644 index 0000000..b37da01 --- /dev/null +++ b/cookbook/c01/p20_combine_map.py @@ -0,0 +1,49 @@ +#!/usr/bin/env python +# -*- encoding: utf-8 -*- +""" +Topic: 合并多个字典或映射 +Desc : +""" + +from collections import ChainMap + + +def combine_map(): + a = {'x': 1, 'z': 3 } + b = {'y': 2, 'z': 4 } + c = ChainMap(a,b) + print(c['x']) # Outputs 1 (from a) + print(c['y']) # Outputs 2 (from b) + print(c['z']) # Outputs 3 (from a) + + print(len(c)) + print(list(c.keys())) + print(list(c.values())) + + c['z'] = 10 + c['w'] = 40 + del c['x'] + print(a) + # del c['y'] + + values = ChainMap() + values['x'] = 1 + # Add a new mapping + values = values.new_child() + values['x'] = 2 + # Add a new mapping + values = values.new_child() + values['x'] = 3 + print(values) + print(values['x']) + # Discard last mapping + values = values.parents + print(values['x']) + # Discard last mapping + values = values.parents + print(values['x']) + print(values) + + +if __name__ == '__main__': + combine_map() \ No newline at end of file diff --git a/source/c01/p20_combine_multiple_map_to_single_map.rst b/source/c01/p20_combine_multiple_map_to_single_map.rst index 3e8d065..cc47f08 100644 --- a/source/c01/p20_combine_multiple_map_to_single_map.rst +++ b/source/c01/p20_combine_multiple_map_to_single_map.rst @@ -1,22 +1,137 @@ -================================ -1.20 合并多个字典为一个 -================================ +============================ +1.20 合并多个字典或映射 +============================ ---------- 问题 ---------- -todo... +现在有多个字典或者映射,你想将它们从逻辑上合并为一个单一的映射后执行某些操作, +比如查找值或者检查某些键是否存在。 | ---------- 解决方案 ---------- -todo... +加入你有如下两个字典: + +.. code-block:: python + + a = {'x': 1, 'z': 3 } + b = {'y': 2, 'z': 4 } + +现在假设你必须在两个字典中执行查找操作(比如先从a中找,如果找不到再在b中找)。 +一个非常简单扼解决方案就是使用collections模块中的ChainMap类。比如: + +.. code-block:: python + + from collections import ChainMap + c = ChainMap(a,b) + print(c['x']) # Outputs 1 (from a) + print(c['y']) # Outputs 2 (from b) + print(c['z']) # Outputs 3 (from a) | ---------- 讨论 ---------- -todo... +一个ChainMap接受多个字典并将它们在逻辑上变为一个字典。 +然后,这些字典并不是真的合并在一起了,ChainMap类只是在内部创建了一个容纳这些字典的列表 +并重新定义了一些常见的字典操作来遍历这个列表。大部分字典操作都是可以正常使用的,比如: + +.. code-block:: python + + >>> len(c) + 3 + >>> list(c.keys()) + ['x', 'y', 'z'] + >>> list(c.values()) + [1, 2, 3] + >>> + +如果出现重复键,那么第一次出现的映射值会被返回。 +因此,例子程序中的c['z']总是会返回字典a中对应的值,而不是b中对应的值。 + +对于字典的更新或删除操作总是影响的是列表中第一个字典。比如: + +.. code-block:: python + + >>> c['z'] = 10 + >>> c['w'] = 40 + >>> del c['x'] + >>> a + {'w': 40, 'z': 10} + >>> del c['y'] + Traceback (most recent call last): + ... + KeyError: "Key not found in the first mapping: 'y'" + >>> + +ChainMap对于编程语言用范围的变量中有作(比如globals, locals等)是非常有用的。 +事实上,有一些方法使它变得简单: + +.. code-block:: python + + >>> values = ChainMap() + >>> values['x'] = 1 + >>> # Add a new mapping + >>> values = values.new_child() + >>> values['x'] = 2 + >>> # Add a new mapping + >>> values = values.new_child() + >>> values['x'] = 3 + >>> values + ChainMap({'x': 3}, {'x': 2}, {'x': 1}) + >>> values['x'] + 3 + >>> # Discard last mapping + >>> values = values.parents + >>> values['x'] + 2 + >>> # Discard last mapping + >>> values = values.parents + >>> values['x'] + 1 + >>> values + ChainMap({'x': 1}) + >>> + +作为ChainMap的替代,你可能会考虑使用update()方法将两个字典合并。比如: + +.. code-block:: python + + >>> a = {'x': 1, 'z': 3 } + >>> b = {'y': 2, 'z': 4 } + >>> merged = dict(b) + >>> merged.update(a) + >>> merged['x'] + 1 + >>> merged['y'] + 2 + >>> merged['z'] + 3 + >>> + +这样也能行得通,但是它需要你创建一个完全不同的字典对象(或者是破坏现有字典结构)。 +同时,如果原字典做了更新,这种改变不会反应到新的合并字典中去。比如: + +.. code-block:: python + + >>> a['x'] = 13 + >>> merged['x'] + 1 + +ChianMap使用原来的字典,它自己不创建新的字典。所以它并不会产生上面所说的结果,比如: + +.. code-block:: python + + >>> a = {'x': 1, 'z': 3 } + >>> b = {'y': 2, 'z': 4 } + >>> merged = ChainMap(a, b) + >>> merged['x'] + 1 + >>> a['x'] = 42 + >>> merged['x'] # Notice change to merged dicts + 42 + >>>