# `collections` --- 容器資料型態

原始碼： Lib/collections/__init__.py

這個模組實作了一些特別的容器資料型態，用來替代 Python 一般內建的容器，例如 `dict` 、 `list` 、 `set` 和 `tuple` 。

`namedtuple()` | 用來建立一個欄位擁有名字的 tuple 子類別的函數
`deque` | 一個類似 list 的容器，可以快速的在頭尾加入元素與取出元素。
`ChainMap` | 一個像是 dict 的類別，用來為多個 mapping 建立單一的 view 。
`Counter` | dict 的子類別，用來計算可 hash 物件的數量。
`OrderedDict` | dict 的子類別，會記錄物件被加入的順序。
`defaultdict` | dict 的子類別，在值不存在 dict 當中時會呼叫一個產生函式。
`UserDict` | 封装了字典对象，简化了字典子类化
`UserList` | 封装了列表对象，简化了列表子类化
`UserString` | 封装了字符串对象，简化了字符串子类化

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

## `ChainMap` objects

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']

也參考

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

- Django 中用于模板的 Context class 是只读的映射链。 它还具有上下文推送和弹出特性，类似于 `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 物件擁有一個字典的使用介面，除了遇到 Counter 中沒有的值時會回傳計數 0 取代 `KeyError` 這點不同。

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

  將一個值的計數設為 0 並不會真的從 counter 中刪除這個元素，使用 `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 对象进行数学运算时同样会保持顺序。 结果会先按每个元素在运算符左边的出现时间排序，然后再按其在运算符右边的出现时间排序。

  除了字典的方法外，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`。

### `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 后进先出的顺序返回键值对，否则就按 FIFO 先进先出的顺序返回键值对。

  `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()` 方法。

### `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):
            if key in self:
                self.move_to_end(key)
            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`。