python / 3.7.2rc1 / all / library-collections.html

集合—容器数据类型

源代码: Lib/collections/init.py


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

namedtuple() 使用命名字段创建 Tuples 子类的工厂函数
deque 列表式容器,两端都有快速追加和弹出
ChainMap 类似 dict 的类,用于创建多个 Map 的单个视图
Counter dict 子类,用于计算可哈希对象
OrderedDict 记住命令条目已添加的 dict 子类
defaultdict dict 子类,调用工厂函数以提供缺失值
UserDict 字典对象周围的包装器,以便于 dict 子类化
UserList 列表对象周围的包装器,以简化列表子类化
UserString 字符串对象周围的包装器,以简化字符串子类化

从版本 3.3 开始不推荐使用,将在版本 3.10 中删除:将集合抽象 Base Class移至collections.abc模块。为了向后兼容,它们在 Python 3.9 中仍然可以在此模块中看到。

ChainMap objects

版本 3.3 中的新Function。

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

该类可用于模拟嵌套作用域,在模板化中很有用。

  • 类别 collections. ChainMap(*Map)
    • ChainMap将多个字典或其他 Map 分组在一起,以创建一个可更新的视图。如果未指定* maps *,则提供一个空字典,以便新链始终至少具有一个 Map。

基础 Map 存储在列表中。该列表是公开的,可以使用* maps *属性进行访问或更新。没有其他状态。

查找 Sequences 搜索基础 Map,直到找到密钥为止。相反,写入,更新和删除仅对第一个 Map 起作用。

ChainMappass引用合并了基础 Map。因此,如果其中一个基础 Map 得到更新,则这些更改将反映在ChainMap中。

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

  • maps

    • 用户可更新的 Map 列表。该列表按从最先搜索到最后搜索的 Sequences 排列。它是唯一的存储状态,可以修改以更改要搜索的 Map。该列表应始终至少包含一个 Map。
  • new_child(* m = None *)

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

在版本 3.4 中更改:添加了可选的m参数。

  • parents
    • 属性会返回一个新的ChainMap,其中包含当前实例中除第一个实 exception 的所有 Map。这对于跳过搜索中的第一张 Map 很有用。用例类似于nested scopes中使用的nonlocal关键字。用例也与内置super()函数的用例 Parallel。对d.parents的引用等效于:ChainMap(*d.maps[1:])

请注意,ChainMap()的迭代 Sequences 是pass扫描从最后到第一的 Map 来确定的:

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

这与从最后一个 Map 开始的一系列dict.update()调用具有相同的 Sequences:

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

See also

ChainMap 示例和食谱

本节显示使用链式 Map 的各种方法。

模拟 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 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类仅对链中的第一个 Map 进行更新(写入和删除),而查找将搜索整个链。但是,如果需要深度写入和删除操作,则可以很容易地创建一个子类来更新在链中更深处找到的密钥:

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 objects

提供了一种计数器工具来支持便捷而快速的计数。例如:

>>> # 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)]
  • 类别 collections. Counter([可迭代的 Map])
    • Counterdict子类,用于计算可哈希对象。它是一个集合,其中元素存储为字典键,其计数存储为字典值。计数可以是任何整数值,包括零或负计数。 Counter类类似于其他语言中的包或多集。

元素是从* iterable 计数的,或者是从另一个 mapping *(或计数器)初始化的:

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

计数器对象具有一个字典接口,只是它们为缺失的项目返回零计数,而不是引发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 版中的新Function。

在 3.7 版中进行了更改:Counter作为dict子类,继承了记住插入 Sequences 的Function。 * Counter *对象上的 math 运算也保留 Sequences。根据在左操作数中首先遇到元素的时间,然后按照在右操作数中遇到的 Sequences,对结果进行排序。

除了适用于所有词典的方法外,计数器对象还支持三种方法:

  • elements ( )
    • 在元素上返回一个迭代器,并重复其次数。元素按首先遇到的 Sequences 返回。如果一个元素的数量少于一个,elements()将忽略它。
>>> c = Counter(a=4, b=2, c=0, d=-2)
>>> sorted(c.elements())
['a', 'a', 'a', 'a', 'b', 'b']
  • most_common([* n *])
    • 返回* n 最常见元素的列表,以及从最常见元素到最少元素的计数。如果Ellipsis n None,则most_common()返回计数器中的 all *个元素。具有相等计数的元素按首先遇到的 Sequences 排序:
>>> Counter('abracadabra').most_common(3)
[('a', 5), ('b', 2), ('r', 2)]
  • subtract([[* iterable-or-mapping *])
    • 从* iterable 或另一个 mapping *(或计数器)中减去元素。类似于dict.update(),但是减去计数而不是替换它们。Importing 和输出都可以为零或负。
>>> 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 版中的新Function。

常用的字典方法可用于Counter个对象,除了两个计数器的工作方式不同。

  • fromkeys(* iterable *)

  • update([[* iterable-or-mapping *])

    • 元素是从* iterable 计数的,或者是从另一个 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

提供了几种 math 运算来组合Counter对象以产生多集(计数大于零的计数器)。加减法pass增加或减少相应元素的计数来组合计数器。交集和并集返回相应计数的最小值和最大值。每个操作都可以接受带符号计数的 Importing,但是输出将排除计数为零或更少的结果。

>>> 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 版的新增Function:添加了对一元加号,一元减号和就地多集操作的支持。

Note

计数器主要用于与正整数一起表示运行计数。但是,请注意不要不必要地排除需要其他类型或负值的用例。为了帮助解决这些用例,本节介绍了最小范围和类型限制。

  • Counter类本身是一个字典子类,对其键和值没有限制。这些值旨在为代表计数的数字,但是您可以*在值字段中存储任何内容。

  • most_common()方法仅要求值是可排序的。

  • 对于c[key] += 1之类的就地操作,值类型仅需要支持加法和减法。因此,分数,浮点数和小数将起作用,并且支持负值。 update()subtract()的情况也相同,它们允许 Importing 和输出均为负值和零值。

  • 多重集方法仅设计用于具有正值的用例。Importing 可以为负或零,但仅创建具有正值的输出。没有类型限制,但是值类型需要支持加,减和比较。

  • elements()方法需要整数计数。它忽略零和负计数。

See also

  • Bag class在 Smalltalk 中。

  • Multisets的维基百科条目。

  • C++ multisets带有示例的教程。

  • 有关多集及其用例的 math 运算,请参阅* Knuth,Donald。 《计算机编程艺术》第二卷,第 4.6.3 节,练习 19 *。

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

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

deque objects

    • class * collections. deque([* iterable * [,* maxlen *]])
    • 返回一个新的 deque 对象,该对象使用* iterable 的数据从左到右初始化(使用append())。如果未指定 iterable *,则新 deque 为空。

deque 是堆栈和队列的概括(名称发音为“ deck”,是“deque”的缩写)。deque 从 deque 的任一侧支持线程安全,内存高效的追加和弹出,并且在任一方向上的性能都大致相同。

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

如果未指定* maxlen *或None,则 deque 可能增长到任意长度。否则,deque 限制为指定的最大长度。一旦限定长度的 deque 已满,则在添加新项目时,将从另一端丢弃相应数量的项目。有界长度 deque 提供的Function类似于 Unix 中的tail过滤器。它们对于跟踪事务和仅关注最近活动的其他数据池也很有用。

deque 对象支持以下方法:

  • append(* x *)

    • 在 deque 的右侧添加* x *。
  • appendleft(* x *)

    • 在 deque 的左侧添加* x *。
  • clear ( )

    • 从 deque 中删除所有元素,使其长度为 0.
  • copy ( )

    • 创建 deque 的浅表副本。

3.5 版中的新Function。

  • count(* x *)
    • 计算等于* x *的 deque 元素的数量。

3.2 版中的新Function。

  • extend(* iterable *)

    • pass添加来自 iterable 参数的元素来扩展 deque 的右侧。
  • extendleft(* iterable *)

    • pass添加* iterable *中的元素来扩展 deque 的左侧。请注意,一系列的左附加结果会颠倒可迭代参数中元素的 Sequences。
  • index(* x * [,* start * [,* stop *]])

    • 返回* x 在 deque 中的位置(在索引 start 或之后,索引 stop *之前或之后)。返回第一个 match,如果找不到则加注ValueError

3.5 版中的新Function。

  • insert(* i x *)
    • 将* x 插入 deque 中的位置 i *。

如果插入会导致有界 deque 超出* maxlen *,则引发IndexError

3.5 版中的新Function。

  • pop ( )

    • 从 deque 的右侧删除并返回一个元素。如果不存在任何元素,则引发IndexError
  • popleft ( )

    • 从 deque 的左侧删除并返回一个元素。如果不存在任何元素,则引发IndexError
  • remove(* value *)

    • 删除第一次出现的* value *。如果找不到,则引发ValueError
  • reverse ( )

    • 就地反转 deque 的元素,然后返回None

3.2 版中的新Function。

  • rotate(* n = 1 *)
    • 向右旋转 deque* n 步骤。如果 n *为负,则向左旋转。

当 deque 不为空时,向右旋转一个步骤等效于d.appendleft(d.pop()),向左旋转一个步骤等效于d.append(d.popleft())

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

  • maxlen
    • deque 的最大大小;如果不受限制,则为None

3.1 版中的新Function。

除上述内容外,deque 支持迭代,Pickling,len(d)reversed(d)copy.copy(d)copy.deepcopy(d),使用in运算符进行成员资格测试以及下标引用(例如d[0])以访问第一个元素。索引访问在两端均为 O(1),但在中间则减慢为 O(n)。为了快速随机访问,请改用列表。

从版本 3.5 开始,deque 支持__add__()__mul__()__imul__()

Example:

>>> 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 Recipes

本节显示使用 deque 的各种方法。

有界长度 deque 提供的Function类似于 Unix 中的tail过滤器:

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

使用 deque 的另一种方法是pass追加到右侧并弹出到左侧来维护一系列最近添加的元素:

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

round-robin scheduler可以使用存储在deque中的 Importing 迭代器来实现。值从零位置的活动迭代器得出。如果该迭代器已用尽,则可以使用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切片和删除的方法。例如,del d[n]的纯 Python 实现依靠rotate()方法来定位要弹出的元素:

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

要实现deque切片,请使用类似的方法应用rotate()将目标元素移到 deque 的左侧。使用popleft()删除旧条目,使用extend()添加新条目,然后反转旋转。pass对该方法进行较小的更改,可以轻松实现 Forth 样式的堆栈操作,例如dupdropswapoverpickrotroll

defaultdict objects

    • class * collections. defaultdict([* default_factory * [,* ... *]])
    • 返回一个新的类似字典的对象。 defaultdict是内置dict类的子类。它重写一种方法并添加一个可写的实例变量。其余Function与dict类的Function相同,在此未记录。

第一个参数提供default_factory属性的初始值;它默认为None。其余所有参数都与传递给dict构造函数一样,包括关键字参数。

defaultdict对象除了标准dict操作之外还支持以下方法:

如果default_factory不是None,则在不带参数的情况下调用它以提供给定* key 的默认值,此值将插入 key *的字典中并返回。

如果调用default_factory引发异常,则此异常将不更改地传播。

当找不到请求的密钥时,此方法由dict类的getitem()方法调用;请参见getitem()返回或引发的任何东西都将返回或引发。

请注意,除getitem()之外,没有其他操作被调用missing()。这意味着get()像普通词典一样,将默认返回None而不是使用default_factory

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

  • default_factory
    • missing()方法使用此属性;它从第一个参数初始化为构造函数(如果存在),或者初始化为None(如果不存在)。

defaultdict Examples

使用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])]

首次遇到每个键时,它尚未在 Map 中;因此,使用default_factory函数自动创建一个条目,该函数返回一个空的listlist.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)]

首次遇到字母时,Map 中会缺少它,因此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})]

namedtuple()具有命名字段的 Tuples 的工厂函数

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

  • collections. namedtuple(* typename field_names **,* rename = False defaults = None module = None *)
    • 返回一个名为* typename *的新 Tuples 子类。新的子类用于创建类似 Tuples 的对象,这些对象具有可pass属性查找访问的字段以及可索引和可迭代的字段。子类的实例还有一个有用的文档字符串(带有 typename 和 field_names)和一个有用的repr()方法,该方法以name=value格式列出 Tuples 的内容。
  • field_names 是一系列字符串,例如['x', 'y']。或者, field_names *可以是单个字符串,每个字段名用空格和/或逗号分隔,例如'x y''x, y'

除下划线开头的名称外,任何有效的 Python 标识符均可用于字段名。有效标识符由字母,数字和下划线组成,但不能以数字或下划线开头,并且不能为keyword,例如* class for return global pass raise * 。

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

  • defaults 可以是Noneiterable的默认值。由于具有默认值的字段必须位于没有默认值的任何字段之后,因此 defaults *将应用于最右边的参数。例如,如果字段名是['x', 'y', 'z']且默认值是(1, 2),那么x将是必填参数,y将默认为1,而z将默认为2

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

命名 Tuples 实例没有按实例的字典,因此它们是轻量级的,并且不需要比常规 Tuples 更多的内存。

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

在版本 3.6 中更改:* verbose rename *参数变为keyword-only arguments

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

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

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)

除了从 Tuples 继承的方法外,命名 Tuples 还支持三个附加方法和两个属性。为避免与字段名称冲突,方法和属性名称以下划线开头。

  • 类方法 somenamedtuple. _make(可迭代)
    • 从现有序列中创建新实例或可迭代的类方法。
>>> t = [11, 22]
>>> Point._make(t)
Point(x=11, y=22)
  • somenamedtuple. _asdict ( )
    • 返回一个新的dict,它将字段名称 Map 到其对应的值:
>>> p = Point(x=11, y=22)
>>> p._asdict()
{'x': 11, 'y': 22}

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

在版本 3.8 中更改:返回常规dict而不是OrderedDict。从 Python 3.7 开始,保证常规命令是有序的。如果需要OrderedDict的其他Function,建议的补救措施是将结果转换为所需的类型:OrderedDict(nt._asdict())

  • somenamedtuple. _replace(*** kwargs *)
    • 返回命名 Tuples 的新实例,用新值替换指定字段:
>>> 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
    • 列出字段名称的字符串 Tuples。对于自省和从现有命名 Tuples 创建新的命名 Tuples 类型很有用。
>>> 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
    • 将字段名 Map 到默认值的字典。
>>> Account = namedtuple('Account', ['type', 'balance'], defaults=[0])
>>> Account._field_defaults
{'balance': 0}
>>> Account('premium')
Account(type='premium', balance=0)

要检索名称存储在字符串中的字段,请使用getattr()函数:

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

要将字典转换为命名的 Tuples,请使用 double-star-operator(如解包参数列表中所述):

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

由于命名的 Tuples 是常规的 Python 类,因此使用子类轻松添加或更改Function。这是添加计算字段和固定宽度打印格式的方法:

>>> 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__设置为空 Tuples。pass防止创建实例字典,这有助于将内存需求保持在较低水平。

子类化对于添加新的存储字段没有用。相反,只需从_fields属性创建一个新的命名 Tuples 类型:

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

可以pass直接分配__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 中更改:属性文档字符串变得可写。

See also

  • 有关为命名 Tuples 添加类型提示的方法,请参见typing.NamedTuple。它还使用class关键字提供了一种优雅的表示法:
class Component(NamedTuple):
part_number: int
weight: float
description: Optional[str] = None
  • 有关基于基础词典而不是 Tuples 的可变名称空间,请参见types.SimpleNamespace()

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

OrderedDict objects

Sequences 词典与常规词典一样,但是具有一些与 Order 操作有关的额外Function。由于内置的dict类具有记住插入 Sequences 的能力(Python 3.7 中保证了这种新行为),因此它们变得不那么重要了。

dict的一些区别仍然存在:

  • 常规dict被设计为非常擅长 Map 操作。跟踪插入 Sequences 是次要的。

  • OrderedDict旨在善于重新排序操作。空间效率,迭代速度和更新操作的性能是次要的。

  • 从算法上讲,OrderedDictdict可以更好地处理频繁的重新排序操作。这使其适合跟踪最近的访问(例如,在LRU cache中)。

  • OrderedDict的相等操作检查匹配 Sequences。

  • OrderedDictpopitem()方法具有不同的签名。它接受一个可选参数来指定要弹出的项目。

  • OrderedDict使用move_to_end()方法可以有效地将元素重新定位到端点。

  • 在 Python 3.8 之前,dict缺少reversed()方法。

  • 类别 collections. OrderedDict([项目])

    • 返回dict子类的实例,该实例具有专门用于重新排列字典 Sequences 的方法。

3.1 版中的新Function。

  • popitem(* last = True *)

    • 有序词典的popitem()方法返回并删除(键,值)对。如果* last *为 true,则对按 LIFOSequences 返回;如果为 false,则按 FIFOSequences 返回。
  • move_to_end(* key last = True *)

    • 将现有的* key 移动到有序词典的任一端。如果 last 为 true(默认值),则该项目移动到右端;如果 last 为 false,则该项目移动到开头。如果 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 版中的新Function。

除了常用的 Map 方法外,有序词典还支持使用reversed()进行反向迭代。

OrderedDict个对象之间的相等性测试对 Sequences 敏感,并被实现为list(od1.items())==list(od2.items())。像常规字典一样,OrderedDict对象与其他Mapping对象之间的相等性测试对 Sequences 不敏感。这样就可以在使用常规词典的任何地方替换OrderedDict个对象。

在版本 3.5 中进行了更改:OrderedDict的项,键和值views现在支持使用reversed()进行反向迭代。

在版本 3.6 中更改:接受 PEP 468,将保留传递给OrderedDict构造函数及其update()方法的关键字参数的 Sequences。

OrderedDict 示例和食谱

创建一个有序的字典变体很容易,该变体可以记住按键的* last *插入 Sequences。如果新条目覆盖现有条目,则原始插入位置将更改并移至末尾:

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 objects

UserDict类充当字典对象的包装器。直接从dict继承子类的能力已部分取代了对此类的需要;但是,由于可以将底层字典作为属性来访问,因此可以更轻松地使用此类。

    • class * collections. UserDict([* initialdata *])
    • 模拟字典的类。实例的内容保存在常规字典中,可passUserDict个实例的data属性进行访问。如果提供了* initialdata ,则data将使用其内容进行初始化;请注意,将不会保留对 initialdata *的引用,允许将其用于其他目的。

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

  • data
    • 一个 true 的字典,用于存储UserDict类的内容。

UserList objects

此类充当列表对象的包装。对于您自己的类似列表的类,这是一个有用的 Base Class,可以从它们继承并覆盖现有方法或添加新方法。这样,可以将新行为添加到列表中。

直接从list继承子类的能力已部分取代了对此类的需要;但是,此类可以更轻松地使用,因为基础列表可以作为属性访问。

  • 类别 collections. UserList([列表])
    • 模拟列表的类。实例的内容保存在常规列表中,可passUserList实例的data属性进行访问。实例的内容最初设置为* list *的副本,默认为空列表[]。 * list *可以是任何可迭代的,例如真实的 Python 列表或UserList对象。

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

  • data
    • 一个实际的list对象,用于存储UserList类的内容。

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

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

UserString objects

UserString类充当字符串对象的包装器。直接从str继承子类的能力已部分取代了对此类的需要;但是,由于可以将基础字符串作为属性访问,因此可以更轻松地使用此类。

  • 类别 collections. UserString(* seq *)
    • 模拟字符串对象的类。实例的内容保存在常规字符串对象中,可passUserString实例的data属性对其进行访问。实例的内容最初设置为* seq *的副本。 * seq *参数可以是可以使用内置str()函数转换为字符串的任何对象。

UserString实例除了支持字符串的方法和操作外,还提供以下属性:

  • data
    • 一个实际的str对象,用于存储UserString类的内容。

在版本 3.5 中进行了更改:新方法__getnewargs____rmod__casefoldformat_mapisprintablemaketrans