8.3. collections
— High-performance container datatypes¶
2.4 新版功能.
Source code: Lib/collections.py and Lib/_abcoll.py
这个模块实现了特定目标的容器,以提供Python标准内建容器 dict
, list
, set
, 和 tuple
的替代选择。
创建命名元组子类的工厂函数 |
2.6 新版功能. |
|
类似列表(list)的容器,实现了在两端快速添加(append)和弹出(pop) |
2.4 新版功能. |
|
字典的子类,提供了可哈希对象的计数功能 |
2.7 新版功能. |
|
字典的子类,保存了他们被添加的顺序 |
2.7 新版功能. |
|
字典的子类,提供了一个工厂函数,为字典查询提供一个默认值 |
2.5 新版功能. |
In addition to the concrete container classes, the collections module provides abstract base classes that can be used to test whether a class provides a particular interface, for example, whether it is hashable or a mapping.
8.3.1. 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])¶ A
Counter
is adict
subclass for counting hashable objects. It is an unordered collection where elements are stored as dictionary keys and their counts are stored as dictionary values. Counts are allowed to be any integer value including zero or negative counts. TheCounter
class is similar to bags or multisets in other languages.元素从一个 iterable 被计数或从其他的 mapping (or counter)初始化:
>>> c = Counter() # a new, empty counter >>> c = Counter('gallahad') # a new counter from an iterable >>> c = Counter({'red': 4, 'blue': 2}) # a new counter from a mapping >>> c = Counter(cats=4, dogs=8) # a new counter from keyword args
Counter对象有一个字典接口,如果引用的键没有任何记录,就返回一个0,而不是弹出一个
KeyError
:>>> c = Counter(['eggs', 'ham']) >>> c['bacon'] # count of a missing element is zero 0
设置一个计数为0不会从计数器中移去一个元素。使用
del
来删除它:>>> c['sausage'] = 0 # counter entry with a zero count >>> del c['sausage'] # del actually removes the entry
2.7 新版功能.
计数器对象除了字典方法以外,还提供了三个其他的方法:
-
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])¶ Return a list of the n most common elements and their counts from the most common to the least. If n is omitted or
None
,most_common()
returns all elements in the counter. Elements with equal counts are ordered arbitrarily:>>> Counter('abracadabra').most_common(3) [('a', 5), ('r', 2), ('b', 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})
通常字典方法都可用于
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 += Counter() # 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})
注解
计数器主要是为了表达运行的正的计数而设计;但是,小心不要预先排除负数或者其他类型。为了帮助这些用例,这一节记录了最小范围和类型限制。
Counter
类是一个字典的子类,不限制键和值。值用于表示计数,但你实际上 可以 存储任何其他值。The
most_common()
method requires only that the values be orderable.For in-place operations such as
c[key] += 1
, the value type need only support addition and subtraction. So fractions, floats, and decimals would work and negative values are supported. The same is also true forupdate()
andsubtract()
which allow negative and zero values for both inputs and outputs.Multiset多集合方法只为正值的使用情况设计。输入可以是负数或者0,但只输出计数为正的值。没有类型限制,但值类型需要支持加,减和比较操作。
The
elements()
method requires integer counts. It ignores zero and negative counts.
参见
Counter class adapted for Python 2.5 and an early Bag recipe for Python 2.4.
Bag class 在 Smalltalk。
Wikipedia 链接 Multisets.
C++ multisets 教程和例子。
数学操作和多集合用例,参考 Knuth, Donald. The Art of Computer Programming Volume II, Section 4.6.3, Exercise 19 。
To enumerate all distinct multisets of a given size over a given set of elements, see
itertools.combinations_with_replacement()
.map(Counter, combinations_with_replacement(‘ABC’, 2)) –> AA AB AC BB BC CC
8.3.2. 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) 内存移动的操作,改变底层数据表达的大小和位置。2.4 新版功能.
如果 maxlen 没有指定或者是
None
,deques 可以增长到任意长度。否则,deque就限定到指定最大长度。一旦限定长度的deque满了,当新项加入时,同样数量的项就从另一端弹出。限定长度deque提供类似Unix filtertail
的功能。它们同样可以用与追踪最近的交换和其他数据池活动。在 2.6 版更改: Added maxlen parameter.
双向队列(deque)对象支持以下方法:
-
append
(x)¶ 添加 x 到右端。
-
appendleft
(x)¶ 添加 x 到左端。
-
clear
()¶ 移除所有元素,使其长度为0.
-
count
(x)¶ 计算 deque 中元素等于 x 的个数。
2.7 新版功能.
-
extend
(iterable)¶ 扩展deque的右侧,通过添加iterable参数中的元素。
-
extendleft
(iterable)¶ 扩展deque的左侧,通过添加iterable参数中的元素。注意,左添加时,在结果中iterable参数中的顺序将被反过来添加。
-
pop
()¶ 移去并且返回一个元素,deque 最右侧的那一个。 如果没有元素的话,就引发一个
IndexError
。
-
popleft
()¶ 移去并且返回一个元素,deque 最左侧的那一个。 如果没有元素的话,就引发
IndexError
。
-
remove
(value)¶ 移除找到的第一个 value。 如果没有的话就引发
ValueError
。2.5 新版功能.
-
reverse
()¶ 将deque逆序排列。返回
None
。2.7 新版功能.
-
rotate
(n=1)¶ 向右循环移动 n 步。 如果 n 是负数,就向左循环。
如果deque不是空的,向右循环移动一步就等价于
d.appendleft(d.pop())
, 向左循环一步就等价于d.append(d.popleft())
。
Deque对象同样提供了一个只读属性:
-
maxlen
¶ Deque的最大尺寸,如果没有限定的话就是
None
。2.7 新版功能.
-
除了以上,deque还支持迭代,清洗,len(d)
, reversed(d)
, copy.copy(d)
, copy.deepcopy(d)
, 成员测试 in
操作符,和下标引用 d[-1]
。索引存取在两端的复杂度是 O(1), 在中间的复杂度比 O(n) 略低。要快速存取,使用list来替代。
示例:
>>> 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. deque
用法¶
这一节展示了deque的多种用法。
限长deque提供了类似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)
The rotate()
method provides a way to implement deque
slicing and
deletion. For example, a pure Python implementation of del d[n]
relies on
the rotate()
method to position elements to be popped:
def delete_nth(d, n):
d.rotate(-n)
d.popleft()
d.rotate(n)
To implement deque
slicing, use a similar approach applying
rotate()
to bring a target element to the left side of the deque. Remove
old entries with popleft()
, add new entries with extend()
, and then
reverse the rotation.
With minor variations on that approach, it is easy to implement Forth style
stack manipulations such as dup
, drop
, swap
, over
, pick
,
rot
, and roll
.
8.3.3. defaultdict
对象¶
-
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 提供一个默认值, 这个值和 key 作为一个对被插入到字典中,并返回。如果调用
default_factory
时抛出了异常,这个异常会原封不动地向外层传递。在无法找到所需键值时,本方法会被
dict
中的__getitem__()
方法调用。无论本方法返回了值还是抛出了异常,都会被__getitem__()
传递。注意
__missing__()
不会 被__getitem__()
以外的其他方法调用。意思就是get()
会向正常的dict那样返回None
,而不是使用default_factory
。
defaultdict
支持以下实例变量:-
default_factory
¶ 这个属性被
__missing__()
方法使用;它从构建器的第一个参数初始化,如果提供了的话,否则就是None
。
-
8.3.3.1. defaultdict
例子¶
Using list
as the default_factory
, it is easy to group a
sequence of key-value pairs into a dictionary of lists:
>>> 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])]
When each key is encountered for the first time, it is not already in the
mapping; so an entry is automatically created using the default_factory
function which returns an empty list
. The list.append()
operation then attaches the value to the new list. When keys are encountered
again, the look-up proceeds normally (returning the list for that key) and the
list.append()
operation adds another value to the list. This technique is
simpler and faster than an equivalent technique using dict.setdefault()
:
>>> d = {}
>>> for k, v in s:
... d.setdefault(k, []).append(v)
...
>>> d.items()
[('blue', [2, 4]), ('red', [1]), ('yellow', [1, 3])]
Setting the default_factory
to int
makes the
defaultdict
useful for counting (like a bag or multiset in other
languages):
>>> s = 'mississippi'
>>> d = defaultdict(int)
>>> for k in s:
... d[k] += 1
...
>>> d.items()
[('i', 4), ('p', 2), ('s', 4), ('m', 1)]
When a letter is first encountered, it is missing from the mapping, so the
default_factory
function calls int()
to supply a default count of
zero. The increment operation then builds up the count for each letter.
The function int()
which always returns zero is just a special case of
constant functions. A faster and more flexible way to create constant functions
is to use itertools.repeat()
which can supply any constant value (not just
zero):
>>> 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>'
Setting the default_factory
to set
makes the
defaultdict
useful for building a dictionary of sets:
>>> 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. namedtuple()
命名元组的工厂函数¶
命名元组赋予每个位置一个含义,提供可读性和自文档性。它们可以用于任何普通元组,并添加了通过名字获取值的能力,通过索引值也是可以的。
-
collections.
namedtuple
(typename, field_names[, verbose=False][, rename=False])¶ 返回一个新的元组子类,名为 typename 。这个新的子类用于创建类元组的对象,可以通过域名来获取属性值,同样也可以通过索引和迭代获取值。子类实例同样有文档字符串(类名和域名)另外一个有用的
__repr__()
方法,以name=value
格式列明了元组内容。field_names 是一个像
[‘x’, ‘y’]
一样的字符串序列。另外 field_names 可以是一个纯字符串,用空白或逗号分隔开元素名,比如'x y'
或者'x, y'
。Any valid Python identifier may be used for a fieldname except for names starting with an underscore. Valid identifiers consist of letters, digits, and underscores but do not start with a digit or underscore and cannot be a
keyword
such as class, for, return, global, pass, print, or raise.如果 rename 为真, 无效域名会自动转换成位置名。比如
['abc', 'def', 'ghi', 'abc']
转换成['abc', '_1', 'ghi', '_3']
, 消除关键词def
和重复域名abc
。If verbose is true, the class definition is printed just before being built.
命名元组实例没有字典,所以它们要更轻量,并且占用更小内存。
2.6 新版功能.
在 2.7 版更改: added support for rename.
示例:
>>> 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)
命名元组尤其有用于赋值 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
In addition to the methods inherited from tuples, named tuples support three additional methods and one attribute. To prevent conflicts with field names, the method and attribute names start with an underscore.
-
classmethod
somenamedtuple.
_make
(iterable)¶ 类方法从存在的序列或迭代实例创建一个新实例。
>>> t = [11, 22] >>> Point._make(t) Point(x=11, y=22)
-
somenamedtuple.
_asdict
()¶ Return a new
OrderedDict
which maps field names to their corresponding values:>>> 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
To convert a dictionary to a named tuple, use the double-star-operator (as described in 解包参数列表):
>>> 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__
为一个空元组。通过阻止创建实例字典保持了较低的内存开销。
Subclassing is not useful for adding new, stored fields. Instead, simply
create a new named tuple type from the _fields
attribute:
>>> Point3D = namedtuple('Point3D', Point._fields + ('z',))
Default values can be implemented by using _replace()
to
customize a prototype instance:
>>> Account = namedtuple('Account', 'owner balance transaction_count')
>>> default_account = Account('<owner name>', 0.0, 0)
>>> johns_account = default_account._replace(owner='John')
Enumerated constants can be implemented with named tuples, but it is simpler and more efficient to use a simple class declaration:
>>> 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)
参见
Named tuple recipe adapted for Python 2.4.
8.3.5. OrderedDict
对象¶
Ordered dictionaries are just like regular dictionaries but they remember the order that items were inserted. When iterating over an ordered dictionary, the items are returned in the order their keys were first added.
-
class
collections.
OrderedDict
([items])¶ Return an instance of a dict subclass, supporting the usual
dict
methods. An OrderedDict is a dict that remembers the order that keys were first inserted. If a new entry overwrites an existing entry, the original insertion position is left unchanged. Deleting an entry and reinserting it will move it to the end.2.7 新版功能.
-
OrderedDict.
popitem
(last=True)¶ The
popitem()
method for ordered dictionaries returns and removes a (key, value) pair. The pairs are returned in LIFO order if last is true or FIFO order if false.
相对于通常的映射方法,有序字典还另外提供了逆序迭代的支持,通过 reversed()
。
Equality tests between OrderedDict
objects are order-sensitive
and are implemented as list(od1.items())==list(od2.items())
.
Equality tests between OrderedDict
objects and other
Mapping
objects are order-insensitive like regular
dictionaries. This allows OrderedDict
objects to be substituted
anywhere a regular dictionary is used.
The OrderedDict
constructor and update()
method both accept
keyword arguments, but their order is lost because Python’s function call
semantics pass-in keyword arguments using a regular unordered dictionary.
参见
Equivalent OrderedDict recipe that runs on Python 2.4 or later.
8.3.5.1. OrderedDict
例子和用法¶
Since an ordered dictionary remembers its insertion order, it can be used in conjunction with sorting to make a sorted dictionary:
>>> # 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)])
The new sorted dictionaries maintain their sort order when entries are deleted. But when new keys are added, the keys are appended to the end and the sort is not maintained.
It is also straight-forward to create an ordered dictionary variant that remembers the order the keys were last inserted. If a new entry overwrites an existing entry, the original insertion position is changed and moved to the end:
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)
An ordered dictionary can be combined with the Counter
class
so that the counter remembers the order elements are first encountered:
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 Abstract Base Classes¶
The collections module offers the following ABCs:
ABC |
Inherits from |
Abstract Methods |
Mixin Methods |
---|---|---|---|
|
|||
|
|||
|
|||
|
|
||
|
|||
|
|||
|
|
||
|
Inherited |
||
|
|
||
|
Inherited |
||
|
|
||
|
Inherited |
||
|
|||
|
|||
|
|||
|
-
class
collections.
Container
¶ -
class
collections.
Hashable
¶ -
class
collections.
Sized
¶ -
class
collections.
Callable
¶ ABCs for classes that provide respectively the methods
__contains__()
,__hash__()
,__len__()
, and__call__()
.
-
class
collections.
Iterable
¶ ABC for classes that provide the
__iter__()
method. See also the definition of iterable.
-
class
collections.
Iterator
¶ ABC for classes that provide the
__iter__()
andnext()
methods. See also the definition of iterator.
-
class
collections.
Sequence
¶ -
class
collections.
MutableSequence
¶ ABCs for read-only and mutable sequences.
-
class
collections.
Mapping
¶ -
class
collections.
MutableMapping
¶ ABCs for read-only and mutable mappings.
-
class
collections.
MappingView
¶ -
class
collections.
ItemsView
¶ -
class
collections.
KeysView
¶ -
class
collections.
ValuesView
¶ ABCs for mapping, items, keys, and values views.
These ABCs allow us to ask classes or instances if they provide particular functionality, for example:
size = None
if isinstance(myvar, collections.Sized):
size = len(myvar)
Several of the ABCs are also useful as mixins that make it easier to develop
classes supporting container APIs. For example, to write a class supporting
the full Set
API, it only necessary to supply the three underlying
abstract methods: __contains__()
, __iter__()
, and __len__()
.
The ABC supplies the remaining methods such as __and__()
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
Notes on using Set
and MutableSet
as a mixin:
Since some set operations create new sets, the default mixin methods need a way to create new instances from an iterable. The class constructor is assumed to have a signature in the form
ClassName(iterable)
. That assumption is factored-out to an internal classmethod called_from_iterable()
which callscls(iterable)
to produce a new set. If theSet
mixin is being used in a class with a different constructor signature, you will need to override_from_iterable()
with a classmethod that can construct new instances from an iterable argument.To override the comparisons (presumably for speed, as the semantics are fixed), redefine
__le__()
and__ge__()
, then the other operations will automatically follow suit.The
Set
mixin provides a_hash()
method to compute a hash value for the set; however,__hash__()
is not defined because not all sets are hashable or immutable. To add set hashability using mixins, inherit from bothSet()
andHashable()
, then define__hash__ = Set._hash
.
参见
OrderedSet recipe for an example built on
MutableSet
.