Python 字典操作进阶

reer 8年前
   <p>学习了 Python 基本的字典操作后,学习这些进阶操作,让写出的代码更加优雅简洁和 pythonic 。</p>    <h2><strong>与字典值有关的计算</strong></h2>    <h3><strong>问题</strong></h3>    <p>想对字典的值进行相关计算,例如找出字典里对应值最大(最小)的项。</p>    <h3><strong>解决方案一:</strong></h3>    <p>假设要从字典 {'a':3, 'b':2, 'c':6} 中找出值最小的项,可以这样做:</p>    <pre>  <code class="language-python">>>> d = {'a':3, 'b':2, 'c':6}  >>> min(zip(d.values(), d.keys()))  (2, 'b')</code></pre>    <p>值得注意的是 d.values() 获取字典的全部值, d.keys() 获取字典的全部键,而且两个序列的顺序依然保持一一对应的关系。因此 zip(d.values(), d.keys()) 实质上生成的是一个 (value, key) 的序列。 min 函数通过比较序列中的元组 (value, key) 找出其最小值。</p>    <h3><strong>解决方案二:</strong></h3>    <p>除了使用 zip(d.values(), d.keys()) 外,还可以使用 dict.items() 方法和生成器推导式来生成 (value, key) 序列,从而传递给 min 函数进行比较:</p>    <pre>  <code class="language-python">>>> d = {'a':3, 'b':2, 'c':6}  >>> min((v ,k) for (k, v) in d.items())  (2, 'b')</code></pre>    <p>这里 min 函数的参数 (v ,k) for (k, v) in d.items() 其实是一个生成器推导式(和列表推导式一样,只是把列表推导式的 [] 改为 () ,而且其返回的一个生成器而非列表),由于生成器推导式做为 min 函数的参数,所以可以省略掉两边的括号(不做为参数时写法应该是 ((v ,k) for (k, v) in d.items()) )。</p>    <h2><strong>字典推导式</strong></h2>    <h3><strong>问题</strong></h3>    <p>想把一个元组列表转换成一个字典,例如把 [('a', 1), ('b', 2), ('c', 3)] 转化为 {'a': 1, 'b': 2, 'c': 3}</p>    <h3><strong>解决方案</strong></h3>    <p>类似于列表推导式,字典推导式可以方便地从其他数据结构构造字典,例如:</p>    <pre>  <code class="language-python">>>> l = [('a', 1), ('b', 2), ('c', 3)]  >>> {k: v for k, v in l}  {'c': 3, 'b': 2, 'a': 1}</code></pre>    <p>字典推导式的规则和列表推导式一样,只是把 [] 换成 {}</p>    <h2><strong>寻找字典的交集</strong></h2>    <h3><strong>问题</strong></h3>    <p>假设有两个字典:</p>    <pre>  <code class="language-python">d1 = {'a':1, 'b':2, 'c':3, 'd':4}  d2 = {'b':2, 'c':3, 'd':3, 'e':5}</code></pre>    <p>要找出这两个字典中具有公共键的项,即要得到结果 {'b':2, 'c':3}</p>    <h3><strong>解决方案</strong></h3>    <p>我们知道一般通过 d.items() 方法来遍历字典, d.items() 方法返回的对象是一个类集合对象,支持集合的基本运算,如取交集、并集等。</p>    <pre>  <code class="language-python">>>> dict(d1.items() & d2.items()) # 取交集  {'b': 2, 'c': 3}</code></pre>    <p>此外, d.keys() 返回字典的键,也是一个类集合对象,如果我们只想找出两个字典中键相同的项,可以这样:</p>    <pre>  <code class="language-python">>>> { k:d1[k] for k in d1.keys() & d2.keys() }  {'b': 2, 'd': 4, 'c': 3}</code></pre>    <p>这里如果相同的键对应不同的值则去第一个字典中的值。推广开来,如果想排除掉字典中的某些键,可以这样:</p>    <pre>  <code class="language-python">>>> { k:d1[k] for k in d1.keys() - {'c', 'd'} } # - 号的含义是集合的差集操作  {'b': 2, 'a': 1}</code></pre>    <p>但有一点需要注意的是, d.values() 返回字典的值,由于字典对应的值不一定唯一,所以 d.values() 一般无法构成一个集合,因此也就不支持一般的集合操作。</p>    <h2><strong>多个字典连接成一个字典</strong></h2>    <h3><strong>问题</strong></h3>    <p>有多个字典,例如:</p>    <pre>  <code class="language-python">d1 = {'a':1, 'b':2, 'c':3}  d2 = {'c':4, 'd':5, 'e':6}</code></pre>    <p>想将这多个字典连接为一个字典,或一次性对多个字典进行迭代操作。</p>    <h3><strong>解决方案</strong></h3>    <p>使用 collections.ChainMap :</p>    <pre>  <code class="language-python">>>> from collections import ChainMap    >>> chain_dict = ChainMap(d1, d2)  >>> for k, v in chain_dict.items():          print(k, v)  a 1  e 6  d 5  c 3  b 2</code></pre>    <p>ChainMap 将传入的多个字典连接为一个字典,并返回一个 ChainMap 对象,这个对象的行为就像一个单一的字典,我们可以对其进行取值或者迭代等操作。注意到这里键 c 对应的值为 3,如果传入 ChainMap 的字典含有相同的键,则对应的值为先传入的字典中的值。</p>    <p>此外,如果你只想单纯地迭代字典的键值对,可以结合使用 items() 和 itertools.chain() 方法:</p>    <pre>  <code class="language-python">>>> from itertools import chain  >>> for k, v in chain(d1.items(), d2.items()):      print(k, v)    a 1  c 3  b 2  e 6  c 4  d 5</code></pre>    <p>这里相同的键会被分别迭代出来。</p>    <h2><strong>保持字典有序</strong></h2>    <h3><strong>问题</strong></h3>    <p>想让字典中元素的迭代顺序和其加入字典的顺序保持一致</p>    <h3><strong>解决方案</strong></h3>    <p>通常来说,使用 d.items() 或者 d.keys() 、 d.values() 方法迭代出来的元素顺序是无法预料的。例如对字典 d = {'a':1, 'b':2, 'c':3} 迭代:</p>    <pre>  <code class="language-python">>>> d = dict()  >>> d['a'] = 1  >>> d['b'] = 2  >>> d['c'] = 3  >>> for k, v in d.items():      print(k, v)    a 1  c 3  b 2</code></pre>    <p>每一次运行结果都可能不同。如果想让元素迭代的顺序和创建字典时元素的顺序一致,就要使用 collections.OrderedDict 代替普通的 dict :</p>    <pre>  <code class="language-python">>>> from collections import OrderedDict  >>> ordered_d = OrderedDict()  >>> ordered_d['a'] = 1  >>> ordered_d['b'] = 2  >>> ordered_d['c'] = 3  >>> for k, v in ordered_d.items():      print(k, v)    a 1  b 2  c 3</code></pre>    <p>OrderedDict 实际通过维护一个双向链表来记录元素添加的顺序,因此其耗费的内存大约为普通字典的两倍。所以在实际使用中需综合考虑各种因素来决定是否使用 OrderedDict 。</p>    <h2><strong>使字典的键映射多个值</strong></h2>    <h3><strong>问题</strong></h3>    <p>通常情况下字典的键只对应一个值。现在想让一个键对应多个值。</p>    <h3><strong>解决方案</strong></h3>    <p>为了使一个键对应多个值,首先需要把多个值放到一个容器中(例如列表或者集合等)。例如有这样一个列表: [('a', 1), ('a', 2), ('b', 3), ('b', 4), ('c', 5)] ,我们要将其转换成一个字典,保持元素的键值对应关系,通常我们会写这样的代码:</p>    <pre>  <code class="language-python">>>> from pprint import pprint  >>> l = [('a', 1), ('a', 2), ('b', 3), ('b', 4), ('c', 5)]  >>> d = {}  >>> for k, v in l:      if k in d:          d[k].append(v)      else:          d[k] = [v]    >>> pprint(d)  {'a': [1, 2], 'b': [3, 4], 'c': [5]}</code></pre>    <p>但是 if else 语句让代码显得有点冗余和不易读, Python 的 defaultdict 改善上述代码。</p>    <pre>  <code class="language-python">>>> from collections import defaultdict  >>> d = defaultdict(list)  >>> for k, v in l:      d[k].append(v)    >>> pprint(d)  defaultdict(<class 'list'>, {'c': [5], 'b': [3, 4], 'a': [1, 2]})</code></pre>    <p>if else 的判语句没有了。</p>    <p>defaultdict 是 dict 的一个子类。对 dict 来说,如果 key 不存在,则 dict[key] 取值操作会抛出 KeyError 异常,但是 defaultdict 则会返回一个传入 defaultdict 构造器的类的实例(例如一个列表)或者自定义的缺失值。因此在上例中,对于 d[k].append(v) ,当 k 不存在时,则会先执行 d[k] = [] 并返回这个空列表,继而将 v 加入到列表中。</p>    <p>传入 defualtdict 构造器的值不一定要是一个类,也可以是一个可调用的函数,当相应的键不在 defualtdict 中时,其默认的值就为这个函数的返回值,例如:</p>    <pre>  <code class="language-python">>>> from collections import defaultdict  >>> def zero_default():      return 0    >>> d = defaultdict(zero_default)  >>> d['a'] = 1  >>> d['a']  1    >>> d['b']  0    >>> d.keys()  dict_keys(['b', 'a'])  >>></code></pre>    <p>利用这样一个特性,我们可以构造无限深度的字典结构:</p>    <pre>  <code class="language-python">>>> from collections import defaultdict  >>> import json  >>> tree = lambda: defaultdict(tree)  >>> d = tree()  >>> d['a']['b'] = 1  >>> print(json.dumps(d)) # 为了显示的格式更好看  {"a": {"b": 1}}</code></pre>    <p>这里当执行 d['a'] 时,由于相应的键不存在,故返回一个 defaultdict(tree) ,当再执行 d['a']['b'] = 1 时,将键 b 对应的值设为 1 。</p>    <p> </p>    <p>来自:http://www.jianshu.com/p/65f4bebc1061</p>    <p> </p>