8.3. 集合 — 容器数据类型 — Python 文档

来自菜鸟教程
Python/docs/3.6/library/collections
跳转至:导航、​搜索

8.3. 收藏 — 容器数据类型

源代码: :source:`Lib/collections/__init__.py`



该模块实现了专门的容器数据类型,为 Python 的通用内置容器 dictlistsettuple 提供了替代方案。

namedtuple() 用于创建具有命名字段的元组子类的工厂函数
deque 类似列表的容器,两端具有快速追加和弹出
ChainMap 用于创建多个映射的单个视图的类 dict 类
Counter 用于计算可散列对象的 dict 子类
OrderedDict 添加了记住订单条目的 dict 子类
defaultdict dict 子类调用工厂函数来提供缺失值
UserDict 围绕字典对象的包装器,以便于 dict 子类化
UserList 列表对象的包装器,以便于列表子类化
UserString 围绕字符串对象的包装器,以便于字符串子类化

3.3 版更改: 集合抽象基类 移至 collections.abc 模块。 为了向后兼容,它们也继续在此模块中可见。


8.3.1. 链表对象

3.3 版中的新功能。


提供了一个 ChainMap 类,用于快速链接多个映射,以便将它们视为一个单元。 它通常比创建新字典并运行多个 update() 调用快得多。

该类可用于模拟嵌套作用域,并可用于模板化。

class collections.ChainMap(*maps)

ChainMap 将多个字典或其他映射组合在一起以创建单个可更新视图。 如果未指定 maps,则提供单个空字典,以便新链始终具有至少一个映射。

底层映射存储在列表中。 该列表是公开的,可以使用 maps 属性访问或更新。 没有其他状态。

查找会连续搜索底层映射,直到找到一个键。 相比之下,写入、更新和删除仅对第一个映射进行操作。

ChainMap 通过引用合并了底层映射。 因此,如果底层映射之一得到更新,这些更改将反映在 ChainMap 中。

支持所有常用的字典方法。 此外,还有一个 maps 属性,一个用于创建新子上下文的方法,以及一个用于访问除第一个映射之外的所有映射的属性:

maps

用户可更新的映射列表。 该列表按从第一个搜索到最后一个搜索的顺序排列。 它是唯一的存储状态,可以修改以更改要搜索的映射。 该列表应始终包含至少一个映射。

new_child(m=None)

返回一个新的 ChainMap,其中包含一个新地图,后跟当前实例中的所有地图。 如果指定了 m,则它成为映射列表最前面的新映射; 如果未指定,则使用空字典,因此对 d.new_child() 的调用等效于:ChainMap({}, *d.maps)。 此方法用于创建可以在不更改任何父映射中的值的情况下更新的子上下文。

3.4 版更改: 增加了可选的 m 参数。

parents

属性返回一个新的 ChainMap,其中包含当前实例中除第一个之外的所有地图。 这对于跳过搜索中的第一个地图很有用。 用例类似于 嵌套作用域 中使用的 nonlocal 关键字的用例。 这些用例还与内置 super() 函数的用例并行。 对 d.parents 的引用相当于:ChainMap(*d.maps[1:])

也可以看看


8.3.1.1. 链表例子和食谱

本节展示了使用链式地图的各种方法。

模拟Python内部查找链的例子:

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}

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']                # Get first key in the chain of contexts
d['x'] = 1            # Set value in current context
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 类只对链中的第一个映射进行更新(写入和删除),而查找将搜索整个链。 但是,如果需要深度写入和删除,很容易创建一个子类来更新链中更深层次的键:

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'})

8.3.2. 柜台对象

提供了一个计数器工具来支持方便和快速的计数。 例如:

>>> # 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 子类,用于计算可散列对象。 它是一个无序集合,其中元素存储为字典键,它们的计数存储为字典值。 计数可以是任何整数值,包括零或负计数。 Counter 类类似于其他语言中的 bag 或 multisets。

元素从 可迭代 计数或从另一个 映射 (或计数器)初始化:

>>> 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 对象有一个字典接口,除了它们为丢失的项目返回零计数而不是引发 KeyError

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

将计数设置为零不会从计数器中删除元素。 使用 del 将其完全删除:

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

3.1 版中的新功能。

Counter 对象支持除所有字典可用的方法之外的三种方法:

elements()

在元素上返回一个迭代器,每个元素的重复次数与其计数相同。 元素以任意顺序返回。 如果元素的计数小于 1,则 elements() 将忽略它。

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

返回 n 最常见元素的列表及其从最常见到最少的计数。 如果省略 nNone,则 most_common() 返回计数器中的 all 元素。 具有相等计数的元素被任意排序:

>>> Counter('abracadabra').most_common(3)  
[('a', 5), ('r', 2), ('b', 2)]
subtract([iterable-or-mapping])

可迭代 或另一个 映射 (或计数器)中减去元素。 像 dict.update() 但减去计数而不是替换它们。 输入和输出都可以为零或负。

>>> 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() 但增加计数而不是替换它们。 此外,iterable 应该是一个元素序列,而不是一个 (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 对象以生成多重集(计数大于零的计数器)。 加法和减法通过增加或减去相应元素的计数来组合计数器。 交集和并集返回对应计数的最小值和最大值。 每个操作都可以接受带符号计数的输入,但输出将排除计数为零或更少的结果。

>>> 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() 也是如此,它们允许输入和输出的负值和零值。
  • 多集方法仅适用于具有正值的用例。 输入可能是负数或零,但只会创建具有正值的输出。 没有类型限制,但是值类型需要支持加减比较。
  • elements() 方法需要整数计数。 它忽略零和负计数。


也可以看看

  • Smalltalk 中的包类

  • Multisets 的维基百科条目。

  • C++ multisets 教程示例。

  • 有关多重集及其用例的数学运算,请参阅克努斯,唐纳德。 计算机编程艺术第二卷,第 4.6.3 节,练习 19 .

  • 要枚举给定元素集上给定大小的所有不同多重集,请参阅 itertools.combinations_with_replacement()

    地图(计数器,combinations_with_replacement('ABC',2))–> AA AB AC BB BC CC


8.3.3. 双端队列对象

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

返回一个从左到右初始化的新 deque 对象(使用 append()),数据来自 iterable。 如果未指定 iterable,则新的双端队列为空。

双端队列是栈和队列的泛化(名称发音为“deck”,是“双端队列”的缩写)。 双端队列支持线程安全、内存高效的追加和从双端队列的任一侧弹出,在任一方向上的 O(1) 性能大致相同。

尽管 list 对象支持类似的操作,但它们针对快速固定长度的操作进行了优化,并为 pop(0)insert(0, v) 操作产生 O(n) 内存移动成本,这改变了底层数据表示的大小和位置。

如果未指定 maxlenNone,则双端队列可能会增长到任意长度。 否则,双端队列受限于指定的最大长度。 一旦有界长度的双端队列已满,当添加新项目时,从另一端丢弃相应数量的项目。 有界长度的双端队列提供类似于 Unix 中的 tail 过滤器的功能。 它们还可用于跟踪仅对最近的活动感兴趣的交易和其他数据池。

Deque 对象支持以下方法:

append(x)

x 添加到双端队列的右侧。

appendleft(x)

x 添加到双端队列的左侧。

clear()

从双端队列中删除所有元素,使其长度为 0。

copy()

创建双端队列的浅拷贝。

3.5 版中的新功能。

count(x)

计算等于 x 的双端队列元素的数量。

3.2 版中的新功能。

extend(iterable)

通过附加来自可迭代参数的元素来扩展双端队列的右侧。

extendleft(iterable)

通过附加来自 iterable 的元素来扩展双端队列的左侧。 请注意,一系列左追加会导致可迭代参数中元素的顺序颠倒。

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

返回 x 在双端队列中的位置(在索引 start 处或之后和索引 stop 之前)。 如果未找到,则返回第一个匹配项或引发 ValueError

3.5 版中的新功能。

insert(i, x)

x 插入双端队列中的位置 i

如果插入会导致有界双端队列增长超过 maxlen,则会引发 IndexError

3.5 版中的新功能。

pop()

从双端队列的右侧移除并返回一个元素。 如果不存在元素,则引发 IndexError

popleft()

从双端队列的左侧移除并返回一个元素。 如果不存在元素,则引发 IndexError

remove(value)

删除第一次出现的 。 如果未找到,则引发 ValueError

reverse()

原地反转双端队列的元素,然后返回 None

3.2 版中的新功能。

rotate(n=1)

向右旋转双端队列 n 步。 如果 n 为负,则向左旋转。

当双端队列不为空时,向右旋转一级相当于d.appendleft(d.pop()),向左旋转一级相当于d.append(d.popleft())

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

maxlen

双端队列的最大大小或 None 如果无界。

3.1 版中的新功能。

除上述之外,双端队列支持迭代、酸洗、len(d)reversed(d)copy.copy(d)copy.deepcopy(d)in的成员资格测试] 运算符,以及下标引用,例如 d[-1]。 索引访问在两端都是 O(1),但在中间变慢到 O(n)。 对于快速随机访问,请改用列表。

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

8.3.3.1. 双端队列食谱

本节展示了使用双端队列的各种方法。

有界长度的双端队列提供类似于 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

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

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

要实现 deque 切片,请使用类似的方法应用 rotate() 将目标元素带到双端队列的左侧。 用 popleft() 删除旧条目,用 extend() 添加新条目,然后反向旋转。 通过对该方法的微小改动,很容易实现 Forth 风格的堆栈操作,例如 dupdropswapover、[ X152X]、rotroll


8.3.4. 默认字典对象

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 的默认值,该值被插入到 的字典中键,并返回。

如果调用 default_factory 引发异常,则此异常将保持不变地传播。

该方法在没有找到请求的key时由dict类的__getitem__()方法调用; 无论它返回或引发什么,然后由 __getitem__() 返回或引发。

请注意,__missing__() 不是 不是 调用除 __getitem__() 之外的任何操作。 这意味着 get() 将像普通字典一样返回 None 作为默认值,而不是使用 default_factory

defaultdict 对象支持以下实例变量:

default_factory

该属性由 __missing__() 方法使用; 它从构造函数的第一个参数(如果存在)或 None(如果不存在)初始化。

8.3.4.1. 默认字典例子

使用 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.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 可用于计数(如其他语言中的包或多重集):

>>> 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() 以提供默认计数为零。 增量操作然后建立每个字母的计数。

总是返回零的函数 int() 只是常量函数的一个特例。 创建常量函数的一种更快、更灵活的方法是使用可以提供任何常量值(不仅仅是零)的 lambda 函数:

>>> 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 可用于构建集合字典:

>>> 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})]

8.3.5. 命名元组() 具有命名字段的元组的工厂函数

命名元组为元组中的每个位置分配含义,并允许更具可读性、自文档化的代码。 它们可以在使用常规元组的任何地方使用,并且它们增加了按名称而不是位置索引访问字段的能力。

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

返回一个名为 typename 的新元组子类。 新的子类用于创建类似元组的对象,这些对象具有可通过属性查找访问的字段以及可索引和可迭代的。 子类的实例也有一个有用的文档字符串(带有 typename 和 field_names)和一个有用的 __repr__() 方法,它以 name=value 格式列出元组内容。

field_names 是一个字符串序列,例如 ['x', 'y']。 或者,field_names 可以是单个字符串,每个字段名由空格和/或逗号分隔,例如 'x y''x, y'

除了以下划线开头的名称外,任何有效的 Python 标识符都可用于字段名。 有效标识符由字母、数字和下划线组成,但不以数字或下划线开头,并且不能是 关键字 ,例如 classfor]returnglobalpassraise

如果 rename 为真,无效的字段名会自动替换为位置名。 例如,将 ['abc', 'def', 'ghi', 'abc'] 转换为 ['abc', '_1', 'ghi', '_3'],消除关键字 def 和重复的字段名 abc

如果 verbose 为真,则在构建后打印类定义。 此选项已过时; 相反,打印 _source 属性更简单。

如果定义了 module,则命名元组的 __module__ 属性设置为该值。

命名元组实例没有每个实例的字典,因此它们是轻量级的,不需要比常规元组更多的内存。

3.1 版更改: 添加了对 重命名 的支持。

3.6 版更改: verboserename 参数变为 仅关键字参数

3.6 版更改: 增加了 模块 参数。

>>> # 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)

命名元组对于将字段名称分配给 csvsqlite3 模块返回的结果元组特别有用:

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()

返回一个新的 OrderedDict,它将字段名称映射到它们对应的值:

>>> p = Point(x=11, y=22)
>>> p._asdict()
OrderedDict([('x', 11), ('y', 22)])

在 3.1 版更改: 返回 OrderedDict 而不是常规的 dict

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._source

带有用于创建命名元组类的纯 Python 源代码的字符串。 源使命名元组自记录。 它可以打印,使用 exec() 执行,或保存到文件并导入。

3.3 版中的新功能。

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)

要检索名称存储在字符串中的字段,请使用 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 版更改: 属性文档字符串变得可写。


可以通过使用 _replace() 自定义原型实例来实现默认值:

>>> Account = namedtuple('Account', 'owner balance transaction_count')
>>> default_account = Account('<owner name>', 0.0, 0)
>>> johns_account = default_account._replace(owner='John')
>>> janes_account = default_account._replace(owner='Jane')

也可以看看


8.3.6. 有序字典对象

有序字典就像普通字典一样,但它们记住插入项目的顺序。 迭代有序字典时,项目将按照它们的键首次添加的顺序返回。

class collections.OrderedDict([items])

返回一个 dict 子类的实例,支持通常的 dict 方法。 OrderedDict 是一个 dict,它记住第一次插入键的顺序。 如果新条目覆盖现有条目,则原始插入位置保持不变。 删除一个条目并重新插入它会将它移到最后。

3.1 版中的新功能。

popitem(last=True)

有序字典的 popitem() 方法返回并删除一个 (key, value) 对。 如果 last 为真,则按 LIFO 顺序返回,如果为假,则按 FIFO 顺序返回。

move_to_end(key, last=True)

将现有的 移动到有序字典的任一端。 如果 last 为真(默认值),则项目移至右端,如果 last 为假,则移至开头。 如果 不存在,则引发 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 的项目、键和值 views 现在支持使用 reversed() 进行反向迭代。


在 3.6 版更改:随着接受 PEP 468,保留传递给 OrderedDict 构造函数及其 [ X178X] 方法。


8.3.6.1. 有序字典例子和食谱

由于有序字典会记住其插入顺序,因此可以与排序结合使用以制作排序字典:

>>> # regular unsorted dictionary
>>> d = {'banana': 3, 'apple': 4, 'pear': 1, 'orange': 2}

>>> # dictionary sorted by key
>>> OrderedDict(sorted(d.items(), key=lambda t: t[0]))
OrderedDict([('apple', 4), ('banana', 3), ('orange', 2), ('pear', 1)])

>>> # dictionary sorted by value
>>> OrderedDict(sorted(d.items(), key=lambda t: t[1]))
OrderedDict([('pear', 1), ('orange', 2), ('banana', 3), ('apple', 4)])

>>> # dictionary sorted by length of the key string
>>> OrderedDict(sorted(d.items(), key=lambda t: len(t[0])))
OrderedDict([('pear', 1), ('apple', 4), ('orange', 2), ('banana', 3)])

删除条目时,新的排序字典会保持其排序顺序。 但是当添加新的键时,键会被附加到末尾并且不会保持排序。

创建一个有序的字典变体也很简单,它记住键 last 插入的顺序。 如果新条目覆盖现有条目,则更改原始插入位置并移至末尾:

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

    def __setitem__(self, key, value):
        if key in self:
            del self[key]
        OrderedDict.__setitem__(self, key, value)

有序字典可以与 Counter 类结合使用,以便计数器记住第一次遇到的顺序元素:

class OrderedCounter(Counter, OrderedDict):
    'Counter that remembers the order elements are first encountered'

    def __repr__(self):
        return '%s(%r)' % (self.__class__.__name__, OrderedDict(self))

    def __reduce__(self):
        return self.__class__, (OrderedDict(self),)

8.3.7. 用户字典对象

UserDict 充当字典对象的包装器。 对此类的需求已部分被直接从 dict 子类化的能力所取代; 但是,这个类更容易使用,因为底层字典可以作为属性访问。

class collections.UserDict([initialdata])

模拟字典的类。 实例的内容保存在常规字典中,可通过 UserDict 实例的 data 属性访问。 如果提供了 initialdata,则用其内容初始化 data; 请注意,不会保留对 initialdata 的引用,允许将其用于其他目的。

除了支持映射的方法和操作外,UserDict 实例还提供以下属性:

data

一个真正的字典,用于存储 UserDict 类的内容。


8.3.8. 用户列表对象

此类充当列表对象的包装器。 它是您自己的类似列表的类的有用基类,可以从它们继承并覆盖现有方法或添加新方法。 通过这种方式,人们可以向列表添加新的行为。

对此类的需求已部分被直接从 list 子类化的能力所取代; 但是,这个类更容易使用,因为底层列表可以作为属性访问。

class collections.UserList([list])

模拟列表的类。 实例的内容保存在一个常规列表中,可以通过 UserList 实例的 data 属性访问该列表。 实例的内容最初设置为 list 的副本,默认为空列表 []list 可以是任何可迭代对象,例如真正的 Python 列表或 UserList 对象。

除了支持可变序列的方法和操作外,UserList 实例还提供以下属性:

data

一个真正的 list 对象,用于存储 UserList 类的内容。

子类化要求: UserList 的子类应提供一个构造函数,该构造函数可以不带参数或一个参数调用。 返回新序列的列表操作尝试创建实际实现类的实例。 为此,它假定可以使用单个参数调用构造函数,该参数是用作数据源的序列对象。

如果派生类不希望遵守此要求,则需要覆盖该类支持的所有特殊方法; 有关在这种情况下需要提供的方法的信息,请咨询来源。


8.3.9. 用户字符串对象

UserString 充当字符串对象的包装器。 对此类的需求已部分被直接从 str 子类化的能力所取代; 但是,这个类更容易使用,因为底层字符串可以作为属性访问。

class collections.UserString([sequence])

模拟字符串或 Unicode 字符串对象的类。 实例的内容保存在一个常规字符串对象中,该对象可通过 UserString 实例的 data 属性访问。 实例的内容最初设置为 序列 的副本。 sequence 可以是 bytesstrUserString(或子类)的实例或可以转换为任意序列使用内置 str() 函数的字符串。

3.5 版本更改:新方法 __getnewargs____rmod__casefoldformat_mapisprintable 和 [ X104X]。