"collections" --- 容器数据类型
******************************

**Source code:** Lib/collections/__init__.py

======================================================================

这个模块实现了特定目标的容器，以提供Python标准内建容器  "dict" ,
"list" , "set" , 和 "tuple" 的替代选择。

+-----------------------+----------------------------------------------------------------------+
| "namedtuple()"        | 创建命名元组子类的工厂函数                                           |
+-----------------------+----------------------------------------------------------------------+
| "deque"               | 类似列表(list)的容器，实现了在两端快速添加(append)和弹出(pop)        |
+-----------------------+----------------------------------------------------------------------+
| "ChainMap"            | 类似字典(dict)的容器类，将多个映射集合到一个视图里面                 |
+-----------------------+----------------------------------------------------------------------+
| "Counter"             | 字典的子类，提供了可哈希对象的计数功能                               |
+-----------------------+----------------------------------------------------------------------+
| "OrderedDict"         | 字典的子类，保存了他们被添加的顺序                                   |
+-----------------------+----------------------------------------------------------------------+
| "defaultdict"         | 字典的子类，提供了一个工厂函数，为字典查询提供一个默认值             |
+-----------------------+----------------------------------------------------------------------+
| "UserDict"            | 封装了字典对象，简化了字典子类化                                     |
+-----------------------+----------------------------------------------------------------------+
| "UserList"            | 封装了列表对象，简化了列表子类化                                     |
+-----------------------+----------------------------------------------------------------------+
| "UserString"          | 封装了列表对象，简化了字符串子类化                                   |
+-----------------------+----------------------------------------------------------------------+

Deprecated since version 3.3, will be removed in version 3.10: 已将 容
器抽象基类 移至 "collections.abc" 模块。 为了保持向下兼容性，它们在
Python 3.9 版的这个模块中仍然存在。


"ChainMap" 对象
===============

3.3 新版功能.

一个 "ChainMap" 类是为了将多个映射快速的链接到一起，这样它们就可以作为
一个单元处理。它通常比创建一个新字典和多次调用 "update()" 要快很多。

这个类可以用于模拟嵌套作用域，并且在模版化的时候比较有用。

class collections.ChainMap(*maps)

   一个 "ChainMap" 将多个字典或者其他映射组合在一起，创建一个单独的可
   更新的视图。  如果没有 *maps* 被指定，就提供一个默认的空字典，这样
   一个新链至少有一个映射。

   底层映射被存储在一个列表中。这个列表是公开的，可以通过 *maps* 属性
   存取和更新。没有其他的状态。

   搜索查询底层映射，直到一个键被找到。不同的是，写，更新和删除只操作
   第一个映射。

   一个 "ChainMap" 通过引用合并底层映射。 所以，如果一个底层映射更新了
   ，这些更改会反映到 "ChainMap" 。

   支持所有常用字典方法。另外还有一个  *maps* 属性(attribute)，一个创
   建子上下文的方法(method)， 一个存取它们首个映射的属性(property):

   maps

      一个可以更新的映射列表。这个列表是按照第一次搜索到最后一次搜索的
      顺序组织的。它是仅有的存储状态，可以被修改。列表最少包含一个映射
      。

   new_child(m=None)

      返回一个新的 "ChainMap" 类，包含了一个新映射(map)，后面跟随当前
      实例的全部映射(map)。如果 "m" 被指定，它就成为不同新的实例，就是
      在所有映射前加上 m，如果没有指定，就加上一个空字典，这样的话一个
      "d.new_child()" 调用等价于 "ChainMap({}, *d.maps)" 。这个方法用
      于创建子上下文，不改变任何父映射的值。

      在 3.4 版更改: 添加了 "m"  可选参数。

   parents

      属性返回一个新的 "ChainMap" 包含所有的当前实例的映射，除了第一个
      。这样可以在搜索的时候跳过第一个映射。 使用的场景类似在 *nested
      scopes* 嵌套作用域中使用 "nonlocal" 关键词。用例也可以类比内建函
      数  "super()" 。一个 "d.parents" 的引用等价于
      "ChainMap(*d.maps[1:])" 。

   注意，一个 "ChainMap()" 的迭代顺序是通过扫描最后的映射来确定的:

      >>> baseline = {'music': 'bach', 'art': 'rembrandt'}
      >>> adjustments = {'art': 'van gogh', 'opera': 'carmen'}
      >>> list(ChainMap(adjustments, baseline))
      ['music', 'art', 'opera']

   这给出了与 "dict.update()" 调用序列相同的顺序，从最后一个映射开始:

      >>> combined = baseline.copy()
      >>> combined.update(adjustments)
      >>> list(combined)
      ['music', 'art', 'opera']

   在 3.9 版更改: Added support for "|" and "|=" operators, specified
   in **PEP 584**.

参见:

  * MultiContext class 在 Enthought CodeTools package 有支持写映射的选
    项。

  * Django 的 Context class 模版是只读映射。它的上下文的push和pop特性
    也类似于 "new_child()" 方法 "parents" 属性。

  * Nested Contexts recipe 提供了是否对第一个映射或其他映射进行写和其
    他修改的选项。

  * 一个 极简的只读版 Chainmap.


"ChainMap" 例子和方法
---------------------

这一节提供了多个使用链映射的案例。

模拟Python内部lookup链的例子

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

让用户指定的命令行参数优先于环境变量，优先于默认值的例子

   import os, argparse

   defaults = {'color': 'red', 'user': 'guest'}

   parser = argparse.ArgumentParser()
   parser.add_argument('-u', '--user')
   parser.add_argument('-c', '--color')
   namespace = parser.parse_args()
   command_line_args = {k: v for k, v in vars(namespace).items() if v is not None}

   combined = ChainMap(command_line_args, os.environ, defaults)
   print(combined['color'])
   print(combined['user'])

用 "ChainMap" 类模拟嵌套上下文的例子

   c = ChainMap()        # Create root context
   d = c.new_child()     # Create nested child context
   e = c.new_child()     # Child of c, independent from d
   e.maps[0]             # Current context dictionary -- like Python's locals()
   e.maps[-1]            # Root context -- like Python's globals()
   e.parents             # Enclosing context chain -- like Python's nonlocals

   d['x'] = 1            # Set value in current context
   d['x']                # Get first key in the chain of contexts
   del d['x']            # Delete from current context
   list(d)               # All nested values
   k in d                # Check all nested values
   len(d)                # Number of nested values
   d.items()             # All nested items
   dict(d)               # Flatten into a regular dictionary

"ChainMap" 类只更新链中的第一个映射，但lookup会搜索整个链。 然而，如果
需要深度写和删除，也可以很容易的通过定义一个子类来实现它

   class DeepChainMap(ChainMap):
       'Variant of ChainMap that allows direct updates to inner scopes'

       def __setitem__(self, key, value):
           for mapping in self.maps:
               if key in mapping:
                   mapping[key] = value
                   return
           self.maps[0][key] = value

       def __delitem__(self, key):
           for mapping in self.maps:
               if key in mapping:
                   del mapping[key]
                   return
           raise KeyError(key)

   >>> d = DeepChainMap({'zebra': 'black'}, {'elephant': 'blue'}, {'lion': 'yellow'})
   >>> d['lion'] = 'orange'         # update an existing key two levels down
   >>> d['snake'] = 'red'           # new keys get added to the topmost dict
   >>> del d['elephant']            # remove an existing key one level down
   >>> d                            # display result
   DeepChainMap({'zebra': 'black', 'snake': 'red'}, {}, {'lion': 'orange'})


"Counter" 对象
==============

一个计数器工具提供快速和方便的计数。比如

   >>> # Tally occurrences of words in a list
   >>> cnt = Counter()
   >>> for word in ['red', 'blue', 'red', 'green', 'blue', 'blue']:
   ...     cnt[word] += 1
   >>> cnt
   Counter({'blue': 3, 'red': 2, 'green': 1})

   >>> # Find the ten most common words in Hamlet
   >>> import re
   >>> words = re.findall(r'\w+', open('hamlet.txt').read().lower())
   >>> Counter(words).most_common(10)
   [('the', 1143), ('and', 966), ('to', 762), ('of', 669), ('i', 631),
    ('you', 554),  ('a', 546), ('my', 514), ('hamlet', 471), ('in', 451)]

class collections.Counter([iterable-or-mapping])

   一个 "Counter" 是一个 "dict" 的子类，用于计数可哈希对象。它是一个集
   合，元素像字典键(key)一样存储，它们的计数存储为值。计数可以是任何整
   数值，包括0和负数。 "Counter" 类有点像其他语言中的 bags或multisets
   。

   元素从一个 *iterable* 被计数或从其他的 *mapping* (or counter)初始化
   ：

   >>> c = Counter()                           # a new, empty counter
   >>> c = Counter('gallahad')                 # a new counter from an iterable
   >>> c = Counter({'red': 4, 'blue': 2})      # a new counter from a mapping
   >>> c = Counter(cats=4, dogs=8)             # a new counter from keyword args

   Counter对象有一个字典接口，如果引用的键没有任何记录，就返回一个0，
   而不是弹出一个 "KeyError" :

   >>> c = Counter(['eggs', 'ham'])
   >>> c['bacon']                              # count of a missing element is zero
   0

   设置一个计数为0不会从计数器中移去一个元素。使用 "del" 来删除它:

   >>> c['sausage'] = 0                        # counter entry with a zero count
   >>> del c['sausage']                        # del actually removes the entry

   3.1 新版功能.

   在 3.7 版更改: 作为 "dict" 的子类，"Counter" 继承了记住插入顺序的功
   能。 *Counter* 对象进行数学运算时同样会保持顺序。 结果会先按每个元
   素在运算符左边的出现时间排序，然后再按其在运算符右边的出现时间排序
   。

   计数器对象除了字典方法以外，还提供了三个其他的方法：

   elements()

      返回一个迭代器，其中每个元素将重复出现计数值所指定次。 元素会按
      首次出现的顺序返回。 如果一个元素的计数值小于一，"elements()" 将
      会忽略它。

      >>> c = Counter(a=4, b=2, c=0, d=-2)
      >>> sorted(c.elements())
      ['a', 'a', 'a', 'a', 'b', 'b']

   most_common([n])

      返回一个列表，其中包含 *n* 个最常见的元素及出现次数，按常见程度
      由高到低排序。 如果 *n* 被省略或为 "None"，"most_common()" 将返
      回计数器中的 *所有* 元素。 计数值相等的元素按首次出现的顺序排序
      ：

      >>> Counter('abracadabra').most_common(3)
      [('a', 5), ('b', 2), ('r', 2)]

   subtract([iterable-or-mapping])

      从 *迭代对象* 或 *映射对象* 减去元素。像 "dict.update()" 但是是
      减去，而不是替换。输入和输出都可以是0或者负数。

      >>> c = Counter(a=4, b=2, c=0, d=-2)
      >>> d = Counter(a=1, b=2, c=3, d=4)
      >>> c.subtract(d)
      >>> c
      Counter({'a': 3, 'b': 0, 'c': -3, 'd': -6})

      3.2 新版功能.

   通常字典方法都可用于 "Counter" 对象，除了有两个方法工作方式与字典并
   不相同。

   fromkeys(iterable)

      这个类方法没有在 "Counter" 中实现。

   update([iterable-or-mapping])

      从 *迭代对象* 计数元素或者 从另一个 *映射对象* (或计数器) 添加。
      像 "dict.update()" 但是是加上，而不是替换。另外，*迭代对象* 应该
      是序列元素，而不是一个 "(key, value)" 对。

"Counter" 对象的常用案例

   sum(c.values())                 # total of all counts
   c.clear()                       # reset all counts
   list(c)                         # list unique elements
   set(c)                          # convert to a set
   dict(c)                         # convert to a regular dictionary
   c.items()                       # convert to a list of (elem, cnt) pairs
   Counter(dict(list_of_pairs))    # convert from a list of (elem, cnt) pairs
   c.most_common()[:-n-1:-1]       # n least common elements
   +c                              # remove zero and negative counts

提供了几个数学操作，可以结合  "Counter" 对象，以生产 multisets (计数器
中大于0的元素）。 加和减，结合计数器，通过加上或者减去元素的相应计数。
交集和并集返回相应计数的最小或最大值。每种操作都可以接受带符号的计数，
但是输出会忽略掉结果为零或者小于零的计数。

>>> c = Counter(a=3, b=1)
>>> d = Counter(a=1, b=2)
>>> c + d                       # add two counters together:  c[x] + d[x]
Counter({'a': 4, 'b': 3})
>>> c - d                       # subtract (keeping only positive counts)
Counter({'a': 2})
>>> c & d                       # intersection:  min(c[x], d[x]) 
Counter({'a': 1, 'b': 1})
>>> c | d                       # union:  max(c[x], d[x])
Counter({'a': 3, 'b': 2})

单目加和减（一元操作符）意思是从空计数器加或者减去。

>>> c = Counter(a=2, b=-4)
>>> +c
Counter({'a': 2})
>>> -c
Counter({'b': 4})

3.3 新版功能: 添加了对一元加，一元减和位置集合操作的支持。

注解:

  计数器主要是为了表达运行的正的计数而设计；但是，小心不要预先排除负数
  或者其他类型。为了帮助这些用例，这一节记录了最小范围和类型限制。

  * "Counter" 类是一个字典的子类，不限制键和值。值用于表示计数，但你实
    际上 *可以* 存储任何其他值。

  * "most_common()" 方法在值需要排序的时候用。

  * 原地操作比如 "c[key] += 1" ， 值类型只需要支持加和减。 所以分数，
    小数，和十进制都可以用，负值也可以支持。这两个方法 "update()" 和
    "subtract()" 的输入和输出也一样支持负数和0。

  * Multiset多集合方法只为正值的使用情况设计。输入可以是负数或者0，但
    只输出计数为正的值。没有类型限制，但值类型需要支持加，减和比较操作
    。

  * "elements()" 方法要求正整数计数。忽略0和负数计数。

参见:

  * Bag class 在 Smalltalk。

  * Wikipedia 链接 Multisets.

  * C++ multisets 教程和例子。

  * 数学操作和多集合用例，参考 *Knuth, Donald. The Art of Computer
    Programming Volume II, Section 4.6.3, Exercise 19* 。

  * 在给定数量和集合元素枚举所有不同的多集合，参考
    "itertools.combinations_with_replacement()"

       map(Counter, combinations_with_replacement('ABC', 2)) # --> AA AB AC BB BC CC


"deque" 对象
============

class collections.deque([iterable[, maxlen]])

   返回一个新的双向队列对象，从左到右初始化(用方法 "append()") ，从
   *iterable* （迭代对象) 数据创建。如果 *iterable* 没有指定，新队列为
   空。

   Deque队列是由栈或者queue队列生成的（发音是 “deck”，”double-ended
   queue”的简称）。Deque 支持线程安全，内存高效添加(append)和弹出(pop)
   ，从两端都可以，两个方向的大概开销都是 O(1) 复杂度。

   虽然 "list" 对象也支持类似操作，不过这里优化了定长操作和 "pop(0)"
   和 "insert(0, v)" 的开销。它们引起 O(n) 内存移动的操作，改变底层数
   据表达的大小和位置。

   如果 *maxlen* 没有指定或者是 "None" ，deques 可以增长到任意长度。否
   则，deque就限定到指定最大长度。一旦限定长度的deque满了，当新项加入
   时，同样数量的项就从另一端弹出。限定长度deque提供类似Unix filter
   "tail" 的功能。它们同样可以用与追踪最近的交换和其他数据池活动。

   双向队列(deque)对象支持以下方法：

   append(x)

      添加 *x* 到右端。

   appendleft(x)

      添加 *x* 到左端。

   clear()

      移除所有元素，使其长度为0.

   copy()

      创建一份浅拷贝。

      3.5 新版功能.

   count(x)

      计算 deque 中元素等于 *x* 的个数。

      3.2 新版功能.

   extend(iterable)

      扩展deque的右侧，通过添加iterable参数中的元素。

   extendleft(iterable)

      扩展deque的左侧，通过添加iterable参数中的元素。注意，左添加时，
      在结果中iterable参数中的顺序将被反过来添加。

   index(x[, start[, stop]])

      返回 *x* 在 deque 中的位置（在索引 *start* 之后，索引 *stop* 之
      前）。 返回第一个匹配项，如果未找到则引发 "ValueError"。

      3.5 新版功能.

   insert(i, x)

      在位置 *i* 插入 *x* 。

      如果插入会导致一个限长 deque 超出长度 *maxlen* 的话，就引发一个
      "IndexError"。

      3.5 新版功能.

   pop()

      移去并且返回一个元素，deque 最右侧的那一个。 如果没有元素的话，
      就引发一个 "IndexError"。

   popleft()

      移去并且返回一个元素，deque 最左侧的那一个。 如果没有元素的话，
      就引发 "IndexError"。

   remove(value)

      移除找到的第一个 *value*。 如果没有的话就引发 "ValueError"。

   reverse()

      将deque逆序排列。返回 "None" 。

      3.2 新版功能.

   rotate(n=1)

      向右循环移动 *n* 步。 如果 *n* 是负数，就向左循环。

      如果deque不是空的，向右循环移动一步就等价于
      "d.appendleft(d.pop())" ， 向左循环一步就等价于
      "d.append(d.popleft())" 。

   Deque对象同样提供了一个只读属性:

   maxlen

      Deque的最大尺寸，如果没有限定的话就是 "None" 。

      3.1 新版功能.

除了以上操作，deque 还支持迭代、封存、"len(d)"、"reversed(d)"、
"copy.copy(d)"、"copy.deepcopy(d)"、成员检测运算符 "in" 以及下标引用例
如通过 "d[0]" 访问首个元素等。 索引访问在两端的复杂度均为 O(1) 但在中
间则会低至 O(n)。 如需快速随机访问，请改用列表。

Deque从版本3.5开始支持 "__add__()", "__mul__()", 和 "__imul__()" 。

示例:

   >>> from collections import deque
   >>> d = deque('ghi')                 # make a new deque with three items
   >>> for elem in d:                   # iterate over the deque's elements
   ...     print(elem.upper())
   G
   H
   I

   >>> d.append('j')                    # add a new entry to the right side
   >>> d.appendleft('f')                # add a new entry to the left side
   >>> d                                # show the representation of the deque
   deque(['f', 'g', 'h', 'i', 'j'])

   >>> d.pop()                          # return and remove the rightmost item
   'j'
   >>> d.popleft()                      # return and remove the leftmost item
   'f'
   >>> list(d)                          # list the contents of the deque
   ['g', 'h', 'i']
   >>> d[0]                             # peek at leftmost item
   'g'
   >>> d[-1]                            # peek at rightmost item
   'i'

   >>> list(reversed(d))                # list the contents of a deque in reverse
   ['i', 'h', 'g']
   >>> 'h' in d                         # search the deque
   True
   >>> d.extend('jkl')                  # add multiple elements at once
   >>> d
   deque(['g', 'h', 'i', 'j', 'k', 'l'])
   >>> d.rotate(1)                      # right rotation
   >>> d
   deque(['l', 'g', 'h', 'i', 'j', 'k'])
   >>> d.rotate(-1)                     # left rotation
   >>> d
   deque(['g', 'h', 'i', 'j', 'k', 'l'])

   >>> deque(reversed(d))               # make a new deque in reverse order
   deque(['l', 'k', 'j', 'i', 'h', 'g'])
   >>> d.clear()                        # empty the deque
   >>> d.pop()                          # cannot pop from an empty deque
   Traceback (most recent call last):
       File "<pyshell#6>", line 1, in -toplevel-
           d.pop()
   IndexError: pop from an empty deque

   >>> d.extendleft('abc')              # extendleft() reverses the input order
   >>> d
   deque(['c', 'b', 'a'])


"deque" 用法
------------

这一节展示了deque的多种用法。

限长deque提供了类似Unix "tail" 过滤功能

   def tail(filename, n=10):
       'Return the last n lines of a file'
       with open(filename) as f:
           return deque(f, n)

另一个用法是维护一个近期添加元素的序列，通过从右边添加和从左边弹出

   def moving_average(iterable, n=3):
       # moving_average([40, 30, 50, 46, 39, 44]) --> 40.0 42.0 45.0 43.0
       # http://en.wikipedia.org/wiki/Moving_average
       it = iter(iterable)
       d = deque(itertools.islice(it, n-1))
       d.appendleft(0)
       s = sum(d)
       for elem in it:
           s += elem - d.popleft()
           d.append(elem)
           yield s / n

一个 轮询调度器 可以通过在 "deque" 中放入迭代器来实现。值从当前迭代器
的位置0被取出并暂存(yield)。 如果这个迭代器消耗完毕，就用 "popleft()"
将其从对列中移去；否则，就通过  "rotate()" 将它移到队列的末尾

   def roundrobin(*iterables):
       "roundrobin('ABC', 'D', 'EF') --> A D E B F C"
       iterators = deque(map(iter, iterables))
       while iterators:
           try:
               while True:
                   yield next(iterators[0])
                   iterators.rotate(-1)
           except StopIteration:
               # Remove an exhausted iterator.
               iterators.popleft()

"rotate()" 方法提供了一种方式来实现 "deque" 切片和删除。 例如， 一个纯
的Python "del d[n]" 实现依赖于 "rotate()" 来定位要弹出的元素

   def delete_nth(d, n):
       d.rotate(-n)
       d.popleft()
       d.rotate(n)

要实现 "deque" 切片， 使用一个类似的方法，应用 "rotate()" 将目标元素放
到左边。通过 "popleft()" 移去老的条目（entries），通过 "extend()" 添加
新的条目， 然后反向 rotate。这个方法可以最小代价实现命令式的栈操作，诸
如 "dup", "drop", "swap", "over", "pick", "rot", 和 "roll" 。


"defaultdict" 对象
==================

class collections.defaultdict([default_factory[, ...]])

   返回一个新的类似字典的对象。 "defaultdict" 是内置 "dict" 类的子类。
   它重载了一个方法并添加了一个可写的实例变量。其余的功能与 "dict" 类
   相同，此处不再重复说明。

   本对象包含一个名为 "default_factory" 的属性，构造时，第一个参数用于
   为该属性提供初始值，默认为 "None"。所有其他参数（包括关键字参数）都
   相当于传递给 "dict" 的构造函数。

   "defaultdict" 对象除了支持标准 "dict" 的操作，还支持以下方法作为扩
   展：

   __missing__(key)

      如果 "default_factory" 属性为 "None"，则调用本方法会抛出
      "KeyError" 异常，附带参数 *key*。

      如果 "default_factory" 不为 "None"，则它会被（不带参数地）调用来
      为 *key* 提供一个默认值，这个值和 *key* 作为一对键值对被插入到字
      典中，并作为本方法的返回值返回。

      如果调用 "default_factory" 时抛出了异常，这个异常会原封不动地向
      外层传递。

      在无法找到所需键值时，本方法会被 "dict" 中的 "__getitem__()" 方
      法调用。无论本方法返回了值还是抛出了异常，都会被 "__getitem__()"
      传递。

      注意，"__missing__()" *不会* 被 "__getitem__()" 以外的其他方法调
      用。意味着 "get()" 会像正常的 dict 那样返回 "None"，而不是使用
      "default_factory"。

   "defaultdict" 对象支持以下实例变量：

   default_factory

      本属性由 "__missing__()" 方法来调用。如果构造对象时提供了第一个
      参数，则本属性会被初始化成那个参数，如果未提供第一个参数，则本属
      性为 "None"。

   在 3.9 版更改: Added merge ("|") and update ("|=") operators,
   specified in **PEP 584**.


"defaultdict" 例子
------------------

使用 "list" 作为 "default_factory"，很轻松地将（键-值对组成的）序列转
换为（键-列表组成的）字典：

>>> s = [('yellow', 1), ('blue', 2), ('yellow', 3), ('blue', 4), ('red', 1)]
>>> d = defaultdict(list)
>>> for k, v in s:
...     d[k].append(v)
...
>>> sorted(d.items())
[('blue', [2, 4]), ('red', [1]), ('yellow', [1, 3])]

当每个键第一次遇见时，它还没有在字典里面，所以自动创建该条目，即调用
"default_factory" 方法，返回一个空的 "list"。 "list.append()" 操作添加
值到这个新的列表里。当再次存取该键时，就正常操作，"list.append()" 添加
另一个值到列表中。这个计数比它的等价方法 "dict.setdefault()" 要快速和
简单：

>>> d = {}
>>> for k, v in s:
...     d.setdefault(k, []).append(v)
...
>>> sorted(d.items())
[('blue', [2, 4]), ('red', [1]), ('yellow', [1, 3])]

设置 "default_factory" 为 "int"，使 "defaultdict" 用于计数（类似其他语
言中的 bag 或 multiset）：

>>> s = 'mississippi'
>>> d = defaultdict(int)
>>> for k in s:
...     d[k] += 1
...
>>> sorted(d.items())
[('i', 4), ('m', 1), ('p', 2), ('s', 4)]

当一个字母首次遇到时，它会查询失败，则 "default_factory" 会调用
"int()" 来提供一个整数 0 作为默认值。后续的自增操作建立起对每个字母的
计数。

函数 "int()" 总是返回 0，这是常数函数的特殊情况。一个更快和灵活的方法
是使用 lambda 函数，可以提供任何常量值（不只是0）：

>>> def constant_factory(value):
...     return lambda: value
>>> d = defaultdict(constant_factory('<missing>'))
>>> d.update(name='John', action='ran')
>>> '%(name)s %(action)s to %(object)s' % d
'John ran to <missing>'

设置 "default_factory" 为 "set" 使 "defaultdict" 用于构建 set 集合：

>>> s = [('red', 1), ('blue', 2), ('red', 3), ('blue', 4), ('red', 1), ('blue', 4)]
>>> d = defaultdict(set)
>>> for k, v in s:
...     d[k].add(v)
...
>>> sorted(d.items())
[('blue', {2, 4}), ('red', {1, 3})]


"namedtuple()" 命名元组的工厂函数
=================================

命名元组赋予每个位置一个含义，提供可读性和自文档性。它们可以用于任何普
通元组，并添加了通过名字获取值的能力，通过索引值也是可以的。

collections.namedtuple(typename, field_names, *, rename=False, defaults=None, module=None)

   返回一个新的元组子类，名为 *typename* 。这个新的子类用于创建类元组
   的对象，可以通过域名来获取属性值，同样也可以通过索引和迭代获取值。
   子类实例同样有文档字符串（类名和域名）另外一个有用的 "__repr__()"
   方法，以 "name=value" 格式列明了元组内容。

   *field_names* 是一个像 "[‘x’, ‘y’]" 一样的字符串序列。另外
   *field_names* 可以是一个纯字符串，用空白或逗号分隔开元素名，比如
   "'x y'" 或者 "'x, y'" 。

   任何有效的Python 标识符都可以作为域名，除了下划线开头的那些。有效标
   识符由字母，数字，下划线组成，但首字母不能是数字或下划线，另外不能
   是关键词 "keyword" 比如 *class*, *for*, *return*, *global*, *pass*,
   或 *raise* 。

   如果 *rename* 为真， 无效域名会自动转换成位置名。比如 "['abc',
   'def', 'ghi', 'abc']" 转换成 "['abc', '_1', 'ghi', '_3']" ， 消除关
   键词 "def"  和重复域名 "abc" 。

   *defaults* 可以为 "None" 或者是一个默认值的 *iterable* 。如果一个默
   认值域必须跟其他没有默认值的域在一起出现，*defaults* 就应用到最右边
   的参数。比如如果域名 "['x', 'y', 'z']" 和默认值 "(1, 2)" ，那么 "x"
   就必须指定一个参数值 ，"y" 默认值 "1" ， "z" 默认值 "2" 。

   如果 *module* 值有定义，命名元组的 "__module__" 属性值就被设置。

   命名元组实例没有字典，所以它们要更轻量，并且占用更小内存。

   在 3.1 版更改: 添加了对 *rename* 的支持。

   在 3.6 版更改: *verbose* 和 *rename* 参数成为 仅限关键字参数.

   在 3.6 版更改: 添加了 *module* 参数。

   在 3.7 版更改: 移除了 *verbose* 形参和 "_source" 属性。

   在 3.7 版更改: 添加了 *defaults* 参数和 "_field_defaults" 属性。

   >>> # Basic example
   >>> Point = namedtuple('Point', ['x', 'y'])
   >>> p = Point(11, y=22)     # instantiate with positional or keyword arguments
   >>> p[0] + p[1]             # indexable like the plain tuple (11, 22)
   33
   >>> x, y = p                # unpack like a regular tuple
   >>> x, y
   (11, 22)
   >>> p.x + p.y               # fields also accessible by name
   33
   >>> p                       # readable __repr__ with a name=value style
   Point(x=11, y=22)

命名元组尤其有用于赋值 "csv"  "sqlite3" 模块返回的元组

   EmployeeRecord = namedtuple('EmployeeRecord', 'name, age, title, department, paygrade')

   import csv
   for emp in map(EmployeeRecord._make, csv.reader(open("employees.csv", "rb"))):
       print(emp.name, emp.title)

   import sqlite3
   conn = sqlite3.connect('/companydata')
   cursor = conn.cursor()
   cursor.execute('SELECT name, age, title, department, paygrade FROM employees')
   for emp in map(EmployeeRecord._make, cursor.fetchall()):
       print(emp.name, emp.title)

除了继承元组的方法，命名元组还支持三个额外的方法和两个属性。为了防止域
名冲突，方法和属性以下划线开始。

classmethod somenamedtuple._make(iterable)

   类方法从存在的序列或迭代实例创建一个新实例。

      >>> t = [11, 22]
      >>> Point._make(t)
      Point(x=11, y=22)

somenamedtuple._asdict()

   返回一个新的 "dict" ，它将字段名称映射到它们对应的值：

      >>> p = Point(x=11, y=22)
      >>> p._asdict()
      {'x': 11, 'y': 22}

   在 3.1 版更改: 返回一个 "OrderedDict" 而不是  "dict" 。

   在 3.8 版更改: 返回一个常规 "dict" 而不是 "OrderedDict"。 因为自
   Python 3.7 起，常规字典已经保证有序。 如果需要 "OrderedDict" 的额外
   特性，推荐的解决方案是将结果转换为需要的类型:
   "OrderedDict(nt._asdict())"。

somenamedtuple._replace(**kwargs)

   返回一个新的命名元组实例，并将指定域替换为新的值

      >>> p = Point(x=11, y=22)
      >>> p._replace(x=33)
      Point(x=33, y=22)

      >>> for partnum, record in inventory.items():
      ...     inventory[partnum] = record._replace(price=newprices[partnum], timestamp=time.now())

somenamedtuple._fields

   字符串元组列出了域名。用于提醒和从现有元组创建一个新的命名元组类型
   。

      >>> p._fields            # view the field names
      ('x', 'y')

      >>> Color = namedtuple('Color', 'red green blue')
      >>> Pixel = namedtuple('Pixel', Point._fields + Color._fields)
      >>> Pixel(11, 22, 128, 255, 0)
      Pixel(x=11, y=22, red=128, green=255, blue=0)

somenamedtuple._field_defaults

   默认值的字典。

      >>> Account = namedtuple('Account', ['type', 'balance'], defaults=[0])
      >>> Account._field_defaults
      {'balance': 0}
      >>> Account('premium')
      Account(type='premium', balance=0)

要获取这个名字域的值，使用 "getattr()" 函数 :

>>> getattr(p, 'x')
11

转换一个字典到命名元组，使用 ** 两星操作符 (所述如 解包参数列表):

>>> d = {'x': 11, 'y': 22}
>>> Point(**d)
Point(x=11, y=22)

因为一个命名元组是一个正常的Python类，它可以很容易的通过子类更改功能。
这里是如何添加一个计算域和定宽输出打印格式:

   >>> class Point(namedtuple('Point', ['x', 'y'])):
   ...     __slots__ = ()
   ...     @property
   ...     def hypot(self):
   ...         return (self.x ** 2 + self.y ** 2) ** 0.5
   ...     def __str__(self):
   ...         return 'Point: x=%6.3f  y=%6.3f  hypot=%6.3f' % (self.x, self.y, self.hypot)

   >>> for p in Point(3, 4), Point(14, 5/7):
   ...     print(p)
   Point: x= 3.000  y= 4.000  hypot= 5.000
   Point: x=14.000  y= 0.714  hypot=14.018

上面的子类设置 "__slots__" 为一个空元组。通过阻止创建实例字典保持了较
低的内存开销。

子类化对于添加和存储新的名字域是无效的。应当通过 "_fields" 创建一个新
的命名元组来实现它:

>>> Point3D = namedtuple('Point3D', Point._fields + ('z',))

文档字符串可以自定义，通过直接赋值给 "__doc__" 属性:

>>> Book = namedtuple('Book', ['id', 'title', 'authors'])
>>> Book.__doc__ += ': Hardcover book in active collection'
>>> Book.id.__doc__ = '13-digit ISBN'
>>> Book.title.__doc__ = 'Title of first printing'
>>> Book.authors.__doc__ = 'List of authors sorted by last name'

在 3.5 版更改: 文档字符串属性变成可写。

参见:

  * 请参阅 "typing.NamedTuple" ，以获取为命名元组添加类型提示的方法。
    它还使用 "class" 关键字提供了一种优雅的符号:

       class Component(NamedTuple):
           part_number: int
           weight: float
           description: Optional[str] = None

  * 对于以字典为底层的可变域名， 参考 "types.SimpleNamespace()" 。

  * "dataclasses" 模块提供了一个装饰器和一些函数，用于自动将生成的特殊
    方法添加到用户定义的类中。


"OrderedDict" 对象
==================

有序词典就像常规词典一样，但有一些与排序操作相关的额外功能。由于内置的
"dict" 类获得了记住插入顺序的能力（在 Python 3.7 中保证了这种新行为）
，它们变得不那么重要了。

一些与 "dict" 的不同仍然存在：

* 常规的 "dict" 被设计为非常擅长映射操作。 跟踪插入顺序是次要的。

* "OrderedDict" 旨在擅长重新排序操作。 空间效率、迭代速度和更新操作的
  性能是次要的。

* 算法上， "OrderedDict" 可以比 "dict" 更好地处理频繁的重新排序操作。
  这使其适用于跟踪最近的访问（例如在 LRU cache 中）。

* 对于 "OrderedDict" ，相等操作检查匹配顺序。

* "OrderedDict" 类的 "popitem()" 方法有不同的签名。它接受一个可选参数
  来指定弹出哪个元素。

* "OrderedDict" 类有一个 "move_to_end()" 方法，可以有效地将元素移动到
  任一端。

* Python 3.8之前， "dict" 缺少 "__reversed__()" 方法。

class collections.OrderedDict([items])

   返回一个 "dict" 子类的实例，它具有专门用于重新排列字典顺序的方法。

   3.1 新版功能.

   popitem(last=True)

      有序字典的 "popitem()" 方法移除并返回一个 (key, value) 键值对。
      如果 *last* 值为真，则按 LIFO (last-in, first-out) 后进先出的顺
      序返回键值对，否则就按 FIFO (first-in, first-out) 先进先出的顺序
      返回键值对。

   move_to_end(key, last=True)

      将现有 *key* 移动到有序字典的任一端。 如果 *last* 为真值（默认）
      则将元素移至末尾；如果  *last* 为假值则将元素移至开头。如果
      *key* 不存在则会触发 "KeyError":

         >>> d = OrderedDict.fromkeys('abcde')
         >>> d.move_to_end('b')
         >>> ''.join(d.keys())
         'acdeb'
         >>> d.move_to_end('b', last=False)
         >>> ''.join(d.keys())
         'bacde'

      3.2 新版功能.

相对于通常的映射方法，有序字典还另外提供了逆序迭代的支持，通过
"reversed()" 。

"OrderedDict" 之间的相等测试是顺序敏感的，实现为
"list(od1.items())==list(od2.items())" 。 "OrderedDict" 对象和其他的
"Mapping" 的相等测试，是顺序敏感的字典测试。这允许 "OrderedDict" 替换
为任何字典可以使用的场所。

在 3.5 版更改: "OrderedDict" 的项(item)，键(key)和值(value) *视图* 现
在支持逆序迭代，通过 "reversed()" 。

在 3.6 版更改: **PEP 468** 赞成将关键词参数的顺序保留， 通过传递给
"OrderedDict" 构造器和它的 "update()" 方法。

在 3.9 版更改: Added merge ("|") and update ("|=") operators,
specified in **PEP 584**.


"OrderedDict" 例子和用法
------------------------

创建记住键值 *最后* 插入顺序的有序字典变体很简单。 如果新条目覆盖现有
条目，则原始插入位置将更改并移至末尾:

   class LastUpdatedOrderedDict(OrderedDict):
       'Store items in the order the keys were last added'

       def __setitem__(self, key, value):
           super().__setitem__(key, value)
           self.move_to_end(key)

一个 "OrderedDict" 对于实现 "functools.lru_cache()" 的变体也很有用:

   class LRU(OrderedDict):
       'Limit size, evicting the least recently looked-up key when full'

       def __init__(self, maxsize=128, /, *args, **kwds):
           self.maxsize = maxsize
           super().__init__(*args, **kwds)

       def __getitem__(self, key):
           value = super().__getitem__(key)
           self.move_to_end(key)
           return value

       def __setitem__(self, key, value):
           super().__setitem__(key, value)
           if len(self) > self.maxsize:
               oldest = next(iter(self))
               del self[oldest]


"UserDict" 对象
===============

"UserDict" 类是用作字典对象的外包装。对这个类的需求已部分由直接创建
"dict" 的子类的功能所替代；不过，这个类处理起来更容易，因为底层的字典
可以作为属性来访问。

class collections.UserDict([initialdata])

   模拟一个字典类。这个实例的内容保存为一个正常字典， 可以通过
   "UserDict" 实例的 "data" 属性存取。如果提供了 *initialdata* 值，
   "data" 就被初始化为它的内容；注意一个 *initialdata* 的引用不会被保
   留作为其他用途。

   "UserDict" 实例提供了以下属性作为扩展方法和操作的支持:

   data

      一个真实的字典，用于保存 "UserDict" 类的内容。


"UserList" 对象
===============

这个类封装了列表对象。它是一个有用的基础类，对于你想自定义的类似列表的
类，可以继承和覆盖现有的方法，也可以添加新的方法。这样我们可以对列表添
加新的行为。

对这个类的需求已部分由直接创建 "list" 的子类的功能所替代；不过，这个类
处理起来更容易，因为底层的列表可以作为属性来访问。

class collections.UserList([list])

   模拟一个列表。这个实例的内容被保存为一个正常列表，通过 "UserList"
   的 "data" 属性存取。实例内容被初始化为一个 *list* 的copy，默认为
   "[]" 空列表。 *list* 可以是迭代对象，比如一个Python列表，或者一个
   "UserList" 对象。

   "UserList" 提供了以下属性作为可变序列的方法和操作的扩展:

   data

      一个 "list" 对象用于存储 "UserList" 的内容。

**子类化的要求:** "UserList" 的子类需要提供一个构造器，可以无参数调用
，或者一个参数调用。返回一个新序列的列表操作需要创建一个实现类的实例。
它假定了构造器可以以一个参数进行调用，这个参数是一个序列对象，作为数据
源。

如果一个分离的类不希望依照这个需求，所有的特殊方法就必须重写；请参照源
代码进行修改。


"UserString" 对象
=================

"UserString" 类是用作字符串对象的外包装。对这个类的需求已部分由直接创
建 "str" 的子类的功能所替代；不过，这个类处理起来更容易，因为底层的字
符串可以作为属性来访问。

class collections.UserString(seq)

   模拟一个字符串对象。这个实例对象的内容保存为一个正常字符串，通过
   "UserString" 的 "data" 属性存取。实例内容初始化设置为 *seq* 的copy
   。*seq* 参数可以是任何可通过内建 "str()" 函数转换为字符串的对象。

   "UserString" 提供了以下属性作为字符串方法和操作的额外支持：

   data

      一个真正的 "str" 对象用来存放 "UserString" 类的内容。

   在 3.5 版更改: 新方法 "__getnewargs__", "__rmod__", "casefold",
   "format_map", "isprintable", 和 "maketrans"。
