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

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

8.3. 收藏 — 高性能容器数据类型

2.4 版中的新功能。


源代码: :source:`Lib/collections.py` and :source:`Lib/_abcoll.py`



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

namedtuple() 用于创建具有命名字段的元组子类的工厂函数

2.6 版中的新功能。


deque 类似列表的容器,两端具有快速追加和弹出

2.4 版中的新功能。


Counter 用于计算可散列对象的 dict 子类

2.7 版中的新功能。


OrderedDict 添加了记住订单条目的 dict 子类

2.7 版中的新功能。


defaultdict dict 子类调用工厂函数来提供缺失值

2.5 版中的新功能。


除了具体的容器类之外,collections 模块还提供了 抽象基类 ,可用于测试类是否提供特定接口,例如,它是否是可散列的或映射。

8.3.1. 柜台对象

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

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

2.7 版中的新功能。

Counter 对象支持三种超出所有字典可用的方法:

elements()

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

>>> c = Counter(a=4, b=2, c=0, d=-2)
>>> list(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})

通常的字典方法可用于 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 += Counter()                  # 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})

笔记

计数器主要设计为使用正整数来表示运行计数; 但是,注意不要不必要地排除需要其他类型或负值的用例。 为了帮助处理这些用例,本节记录了最小范围和类型限制。

  • Counter 类本身是一个字典子类,对其键和值没有限制。 这些值旨在是表示计数的数字,但您 可以 在值字段中存储任何内容。
  • most_common() 方法只要求值是可排序的。
  • 对于c[key] += 1等就地操作,值类型只需要支持加减。 因此分数、浮点数和小数都可以使用,并且支持负值。 update()subtract() 也是如此,它们允许输入和输出的负值和零值。
  • 多集方法仅适用于具有正值的用例。 输入可能是负数或零,但只会创建具有正值的输出。 没有类型限制,但是值类型需要支持加减比较。
  • elements() 方法需要整数计数。 它忽略零和负计数。


也可以看看


8.3.2. 双端队列对象

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

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

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

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

2.4 版中的新功能。

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

2.6 版变更: 添加 maxlen 参数。

Deque 对象支持以下方法:

append(x)

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

appendleft(x)

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

clear()

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

count(x)

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

2.7 版中的新功能。

extend(iterable)

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

extendleft(iterable)

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

pop()

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

popleft()

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

remove(value)

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

2.5 版中的新功能。

reverse()

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

2.7 版中的新功能。

rotate(n=1)

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

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

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

maxlen

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

2.7 版中的新功能。

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

例子:

>>> 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.2.1. 双端队列食谱

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

有界长度的双端队列提供类似于 Unix 中的 tail 过滤器的功能:

def tail(filename, n=10):
    'Return the last n lines of a file'
    return deque(open(filename), 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 / float(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.3. 默认字典对象

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

返回一个新的类似字典的对象。 defaultdict 是内置的 dict 类的子类。 它覆盖了一种方法并添加了一个可写的实例变量。 其余功能与 dict 类相同,此处未记录。

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

2.5 版中的新功能。

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.3.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)
...
>>> d.items()
[('blue', [2, 4]), ('red', [1]), ('yellow', [1, 3])]

第一次遇到每个键时,它尚未在映射中; 因此使用 default_factory 函数自动创建一个条目,该函数返回一个空的 listlist.append() 操作然后将该值附加到新列表。 当再次遇到键时,查找将正常进行(返回该键的列表)并且 list.append() 操作将另一个值添加到列表中。 这种技术比使用 dict.setdefault() 的等效技术更简单、更快:

>>> d = {}
>>> for k, v in s:
...     d.setdefault(k, []).append(v)
...
>>> 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
...
>>> d.items()
[('i', 4), ('p', 2), ('s', 4), ('m', 1)]

当第一次遇到字母时,映射中缺少它,因此 default_factory 函数调用 int() 以提供默认计数为零。 增量操作然后建立每个字母的计数。

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

>>> def constant_factory(value):
...     return itertools.repeat(value).next
>>> 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)
...
>>> d.items()
[('blue', set([2, 4])), ('red', set([1, 3]))]

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

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

collections.namedtuple(typename, field_names[, verbose=False][, rename=False])

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

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

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

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

如果 verbose 为真,则在构建之前打印类定义。

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

2.6 版中的新功能。

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

例子:

>>> Point = namedtuple('Point', ['x', 'y'], verbose=True)
class Point(tuple):
    'Point(x, y)'

    __slots__ = ()

    _fields = ('x', 'y')

    def __new__(_cls, x, y):
        'Create new instance of Point(x, y)'
        return _tuple.__new__(_cls, (x, y))

    @classmethod
    def _make(cls, iterable, new=tuple.__new__, len=len):
        'Make a new Point object from a sequence or iterable'
        result = new(cls, iterable)
        if len(result) != 2:
            raise TypeError('Expected 2 arguments, got %d' % len(result))
        return result

    def __repr__(self):
        'Return a nicely formatted representation string'
        return 'Point(x=%r, y=%r)' % self

    def _asdict(self):
        'Return a new OrderedDict which maps field names to their values'
        return OrderedDict(zip(self._fields, self))

    def _replace(_self, **kwds):
        'Return a new Point object replacing specified fields with new values'
        result = _self._make(map(kwds.pop, ('x', 'y'), _self))
        if kwds:
            raise ValueError('Got unexpected field names: %r' % kwds.keys())
        return result

    def __getnewargs__(self):
        'Return self as a plain tuple.  Used by copy and pickle.'
        return tuple(self)

    __dict__ = _property(_asdict)

    def __getstate__(self):
        'Exclude the OrderedDict from pickling'
        pass

    x = _property(_itemgetter(0), doc='Alias for field number 0')

    y = _property(_itemgetter(1), doc='Alias for field number 1')



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

在 2.7 版中更改: 返回 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._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',))

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

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

枚举常量可以用命名元组来实现,但使用简单的类声明更简单、更有效:

>>> Status = namedtuple('Status', 'open pending closed')._make(range(3))
>>> Status.open, Status.pending, Status.closed
(0, 1, 2)
>>> class Status:
...     open, pending, closed = range(3)

也可以看看

命名元组配方适用于Python 2.4。


8.3.5. 有序字典对象

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

class collections.OrderedDict([items])

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

2.7 版中的新功能。

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

除了通常的映射方法,有序字典还支持使用 reversed() 的反向迭代。

OrderedDict 对象之间的相等性测试是顺序敏感的,并且实现为 list(od1.items())==list(od2.items())OrderedDict 对象和其他 Mapping 对象之间的相等性测试与常规字典一样对顺序不敏感。 这允许在使用常规字典的任何地方替换 OrderedDict 对象。

OrderedDict 构造函数和 update() 方法都接受关键字参数,但它们的顺序丢失了,因为 Python 的函数调用语义使用常规无序字典传入关键字参数。

8.3.5.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.6. 集合抽象基类

collections 模块提供以下 ABCs

美国广播公司 继承自 抽象方法 混合方法
Container __contains__
Hashable __hash__
Iterable __iter__
Iterator Iterable next __iter__
Sized __len__
Callable __call__
Sequence 大小可迭代容器 __getitem____len__ __contains____iter____reversed__indexcount
MutableSequence Sequence __getitem____setitem____delitem____len__insert 继承了 Sequence 方法和 appendreverseextendpopremove 和 [ X103X]
Set 大小可迭代容器 __contains____iter____len__ __le____lt____eq____ne____gt____ge____and__、[ X77X]、__sub____xor__isdisjoint
MutableSet Set __contains____iter____len__adddiscard 继承了 Set 方法和 clearpopremove__ior____iand____ixor__ ] 和 __isub__
Mapping 大小可迭代容器 __getitem____iter____len__ __contains__keysitemsvaluesget__eq____ne__
MutableMapping Mapping __getitem____setitem____delitem____iter____len__ 继承了 Mapping 方法和 poppopitemclearupdatesetdefault
MappingView Sized __len__
ItemsView MappingView, Set __contains____iter__
KeysView MappingView, Set __contains____iter__
ValuesView MappingView __contains____iter__
class collections.Container

class collections.Hashable
class collections.Sized
class collections.Callable

分别提供方法 __contains__()__hash__()__len__()__call__() 的类的 ABC。
class collections.Iterable
提供 __iter__() 方法的类的 ABC。 另见 iterable 的定义。
class collections.Iterator
ABC 用于提供 __iter__()next() 方法的类。 另见迭代器的定义。
class collections.Sequence

class collections.MutableSequence

用于只读和可变 序列 的 ABC。
class collections.Set

class collections.MutableSet

只读和可变集的 ABC。
class collections.Mapping

class collections.MutableMapping

用于只读和可变 映射 的 ABC。
class collections.MappingView

class collections.ItemsView
class collections.KeysView
class collections.ValuesView

用于映射、项目、键和值 视图 的 ABC。

这些 ABC 允许我们询问类或实例是否提供特定功能,例如:

size = None
if isinstance(myvar, collections.Sized):
    size = len(myvar)

一些 ABC 也可用作 mixin,可以更轻松地开发支持容器 API 的类。 例如,要编写一个支持完整的 Set API 的类,只需要提供三个底层抽象方法:__contains__()__iter__()__len__() ]。 ABC 提供剩余的方法,例如 __and__()isdisjoint()

class ListBasedSet(collections.Set):
     ''' Alternate set implementation favoring space over speed
         and not requiring the set elements to be hashable. '''
     def __init__(self, iterable):
         self.elements = lst = []
         for value in iterable:
             if value not in lst:
                 lst.append(value)

     def __iter__(self):
         return iter(self.elements)

     def __contains__(self, value):
         return value in self.elements

     def __len__(self):
         return len(self.elements)

s1 = ListBasedSet('abcdef')
s2 = ListBasedSet('defghi')
overlap = s1 & s2            # The __and__() method is supported automatically

使用 SetMutableSet 作为 mixin 的注意事项:

  1. 由于某些集合操作会创建新集合,因此默认的 mixin 方法需要一种从可迭代对象创建新实例的方法。 假定类构造函数具有 ClassName(iterable) 形式的签名。 该假设被分解为称为 _from_iterable() 的内部类方法,该方法调用 cls(iterable) 以生成新集合。 如果在具有不同构造函数签名的类中使用 Set mixin,则需要使用可以从可迭代参数构造新实例的类方法覆盖 _from_iterable()
  2. 要覆盖比较(大概是为了速度,因为语义是固定的),重新定义 __le__()__ge__(),然后其他操作将自动效仿。
  3. Set mixin 提供了一个 _hash() 方法来计算集合的哈希值; 然而,__hash__() 并未定义,因为并非所有集合都是可散列或不可变的。 要使用 mixin 添加 set hashability,请继承 Set()Hashable(),然后定义 __hash__ = Set._hash

也可以看看