Python Collections详解

collections模块在内置数据结构(list、tuple、dict、set)的基础上,提供了几个额外的数据结构:ChainMap、Counter、deque、defaultdict、namedtuple和OrderedDict等;

  • ChainMap:Python3的新特性,将多个map组成一个新的单元(原来的map结构仍然存在,类似于这些map被存在了一个list中),这比新建一个map再将其他的map用update加起来快得多,ChainMap可以用来模拟嵌套的情景,而且多用于模板之中;
  • Counter:dict的子类,计算可hash对象的简单计算器;
  • deque:类似于list的双端队列,可以同时在list的左边增删元素;
  • defaultdict:在普通dict支上添加了默认工厂函数,使得键不存在时会自动生成相应类型的值;
  • namedtuple:加强版的tuple,最大作用就是给tuple的每个位置的值设置了别名,增加程序可读性;
  • OrderedDict:dict是无序的,但有时我们希望字典是有序的,OrderedDict提供了这项服务,OrderedDict中的键值对是按照它们加入时的顺序存储的;

ChainMap

from collections import ChainMap
# 新建ChainMap及其结构
m1 = {"color": "red", "user": "guest"}
m2 = {"name": "cat", "age": 22}
chain_map = ChainMap(m1, m2)
print(chain_map) # ChainMap({'color': 'red', 'user': 'guest'}, {'name': 'cat', 'age': 22})
print(chain_map.items()) # ItemsView(ChainMap({'color': 'red', 'user': 'guest'}, {'name': 'cat', 'age': 22})) # 获取ChainMap中的元素
print(chain_map.get("name")) # cat
print(chain_map.get("age")) # 22
print(chain_map.get("gender")) # None # 新增
m3 = {"date": "1-6", "gender": "female"}
chain_map = chain_map.new_child(m3)
print(chain_map.items())
# ItemsView(ChainMap({'date': '1-6', 'gender': 'female'}, {'color': 'red', 'user': 'guest'}, {'name': 'cat', 'age': 22})) # parents属性
print(chain_map.parents) # ChainMap({'color': 'red', 'user': 'guest'}, {'name': 'cat', 'age': 22})
print(chain_map.parents.parents) # ChainMap({'name': 'cat', 'age': 22})
print(chain_map.parents.parents.parents) # ChainMap({}) # maps属性
print(chain_map.maps) # [{'date': '1-6', 'gender': 'female'}, {'color': 'red', 'user': 'guest'}, {'name': 'cat', 'age': 22}]
  • 可以传入都多个参数来初始化ChainMap,如果参数为空,会自动加一个空的map;
  • 获取的key如果不存在,则返回None;
  • 可以通过new_child()方法来新增map,新的map添加在最前面;
  • parents属性返回除去第一个map后的ChainMap实例;

Counter

Counter可以支持方便、快速的计数,例如:

from collections import Counter

counter = Counter()
word_list = ["q", "a", "c", "b", "a", "q"]
for word in word_list:
counter[word] += 1
print(counter) # Counter({'q': 2, 'a': 2, 'c': 1, 'b': 1})
print(counter.get("a")) # 2

对可迭代对象进行计数或者从另一个映射(counter)进行初始化:

>>> from collections import Counter
>>> counter = Counter() # 创建空的counter
>>> counter
Counter()
>>> counter = Counter("intelligence") # 从可迭代的字符初始化counter
>>> counter
Counter({'e': 3, 'n': 2, 'i': 2, 'l': 2, 'c': 1, 't': 1, 'g': 1})
>>> counter = Counter({"red": 255, "blu": 235}) # 从map映射初始化counter
>>> counter
Counter({'red': 255, 'blu': 235})
>>> counter = Counter(person=100, cats=22, dogs=33) # 从kwargs参数初始化counter
>>> counter
Counter({'person': 100, 'dogs': 33, 'cats': 22})
>>> counter = Counter([2, 3, 5, 9, 2, 3, 9, 5, 4]) # 从列表书池化counter
>>> counter
Counter({9: 2, 2: 2, 3: 2, 5: 2, 4: 1})

Counter对象类似于字典,如果某个项缺失,会返回0,而不会出现KeyError异常;

>>> counter = Counter(["eggs", "harm"])
>>> counter
Counter({'eggs': 1, 'harm': 1})
>>> counter["hanm"] # 没有"hanm"
0
>>> counter["eggs"] # 有"eggs"
1
>>>

将一个元素的数目设置为0,并不能将它从counter中删除,使用del可以将这个元素删除:

>>> from collections import Counter
>>> counter = Counter({"eggs": 10, "harmer": 12})
>>> counter
Counter({'harmer': 12, 'eggs': 10})
>>> counter["eggs"] = 0
>>> counter
Counter({'harmer': 12, 'eggs': 0}) # "eggs"依然存在
>>> del counter["eggs"]
>>> counter
Counter({'harmer': 12}) # "eggs"不存在

Counter对象支持以下三个字典不支持的方法,elements(),most_common(),substract();

elements()返回一个迭代器,每个元素重复的次数为它的数目,顺序是任意顺序,如果某个元素的数目少于1,那么elements()将会忽略它;

>>> counter = Counter(e=1, b=2, c=0, d=-2, a=3)
>>> counter
Counter({'a': 3, 'b': 2, 'e': 1, 'c': 0, 'd': -2})
>>> counter.elements()
<itertools.chain object at 0x7f7a6c7e40f0>
>>> list(counter.elements())
['a', 'a', 'a', 'b', 'b', 'e']

most_common()返回一个列表,包含counter中n个最大数目的元素,如果忽略n或者为None,most_common()将会返回counter中的所有元素,元素数目相同的将会以任意顺序排序;

>>> most_counter = Counter("ambulance").most_common(3)
>>> most_counter
[('a', 2), ('l', 1), ('e', 1)]
>>> most_counter = Counter("ambulance").most_common()
>>> most_counter
[('a', 2), ('l', 1), ('e', 1), ('c', 1), ('m', 1), ('u', 1), ('n', 1), ('b', 1)]
>>> most_counter = Counter("ambulance").most_common(None)
>>> most_counter
[('a', 2), ('l', 1), ('e', 1), ('c', 1), ('m', 1), ('u', 1), ('n', 1), ('b', 1)]

subtract()从一个可迭代对象中或者另一个映射(或counter)中所有元素相减,类似于dict.update();但subtract()是数目,而不是替换它们,输入和输出都有可能为0或为负;

>>> a_counter = Counter(a=3, b=1, c=-2, d=6, e=0, f=-3)
>>> b_counter = Counter(a=-2, b=0, c=2)
>>> a_counter.subtract(b_counter)
>>> a_counter
Counter({'d': 6, 'a': 5, 'b': 1, 'e': 0, 'f': -3, 'c': -4})
>>> b_counter.subtract(a_counter)
>>> b_counter
Counter({'c': 6, 'f': 3, 'e': 0, 'b': -1, 'd': -6, 'a': -7})

update()从一个可迭代对象中或者另外一个映射(或counter)中所有元素相加,类似于dict.update;但update()是数目相加而非替换它们,另外可迭代对象是一个元素序列,而非(key,value)对构成的序列;

>>> a_counter
Counter({'d': 6, 'a': 5, 'b': 1, 'e': 0, 'f': -3, 'c': -4})
>>> d_counter
Counter({'c': 3, 'b': 2, 'a': 1, 'd': -6})
>>> a_counter.update(d_counter)
>>> a_counter
Counter({'a': 6, 'b': 3, 'e': 0, 'd': 0, 'c': -1, 'f': -3})
>>> d_counter.update(a_counter)
>>> d_counter
Counter({'a': 7, 'b': 5, 'c': 2, 'e': 0, 'f': -3, 'd': -6})

Counter对象常见的操作:

>>> c_counter
Counter({'d': 6, 'a': 3, 'c': 0, 'b': -4})
>>> sum(c_counter.values()) # 统计所有的数目
5
>>> list(c_counter) # 列出所有唯一的元素
['c', 'd', 'b', 'a']
>>> set(c_counter) # 转换为set
{'c', 'a', 'b', 'd'}
>>> dict(c_counter) # 转换为常见的dict
{'c': 0, 'a': 3, 'b': -4, 'd': 6}
>>> c_counter.items() # 转换为(element, counter)对构成的列表
dict_items([('c', 0), ('d', 6), ('b', -4), ('a', 3)])
>>> c_counter.most_common() # Counter中的所有元素
[('d', 6), ('a', 3), ('c', 0), ('b', -4)]
>>> c_counter.most_common()[:-4:-1] # 切片输出
[('b', -4), ('c', 0), ('a', 3)]
>>> c_counter += Counter() # 删除数目为0和负数的元素
>>> c_counter
Counter({'d': 6, 'a': 3})
>>> Counter(dict(c_counter.items())) # 从(element, counter)对构成的列表转换为Counter
Counter({'d': 6, 'a': 3})
>>> c_counter.clear() # 清空Counter
>>> c_counter
Counter()

在Counter对象进行数学操作,得多集合(counter中元素数目大于0)加减法操作,是相加或相减对应元素的数目;交集和并集返回对应数目的最小值和最大值;每个操作均接受有符号的数目,但输出并不包含数目为0或为负的元素;

>>> c = Counter(a=3,b=1,c=-2)
>>> d = Counter(a=1,b=2,c=4)
>>> c+d#求和
Counter({'a': 4, 'b': 3, 'c': 2})
>>> c-d#求差
Counter({'a': 2})
>>> c & d#求交集
Counter({'a': 1, 'b': 1})
>>> c | d#求并集
Counter({'c': 4, 'a': 3, 'b': 2})

deque

deque是栈和队列的一种广义实现。deque是"double-end queue"的简称,deque支持线程安全、有效内存也以近似O(1)的性能在deque的两端插入和删除元素,尽管list也支持相似的操作,但它主要在固定长度操作上的优化,从而在pop(0)和insert(0, v)(会改变数据的位置和大小)上有O(n)的时间复杂度。

deque支持如下方法:

  • append(element):将元素添加到deque的右侧;
  • appendleft(element):将元素添加到deque的左侧;
  • clear():将deque中的元素全部删除,最后长度为0;
  • count(element):返回deque中元素等于element的个数;
  • extend(iterable):将可迭代变量iterable中的元素添加到deque的右侧;
  • extendleft(iterable):将可迭代变量iterable中的元素添加到deque的左侧,往左添加序列的顺序与可迭代变量iterable中的元素相反;
  • pop():移除和返回deque中最右边的元素,如果没有元素,将会产生IndexError异常;
  • popleft():移除和返回deque中最左侧的元素,如果没有元素,将会产生IndexError异常;
  • remove(value):移除第一次出现的value,如果没有找到,将会产生ValueError异常;
  • reverse():反转deque中的元素,并返回None;
  • rotate(n):从右侧反转n步,如果n为负数,则从左侧反转。d.rotate(1)等于d.appendleft(d.pop());
  • maxlen:只读的属性,deque的最大长度,如果无解,就返回None;

除了以上的方法之外,deque还支持迭代、序列化、len(d)、reversed(d)、copy.copy(d)、copy.deepcopy(d),通过in操作符进行成员测试和下标引用,索引的时间复杂度在两端是O(1),在中间是O(n),为了快速获取,可以使用list代替;

>>> from collections import deque
>>> deq = deque("police") # 创建deque
>>> for ele in deq: # 遍历deque
... print(ele.upper())
...
P
O
L
I
C
E
>>> deq.append("r") # deque右侧添加元素
>>> deq.appendleft("s") # deque左侧添加元素
>>> deq
deque(['s', 'p', 'o', 'l', 'i', 'c', 'e', 'r'])
>>> deq.pop() # 返回和移除最右侧元素
'r'
>>> deq.popleft() # 返回和移除最左侧元素
's'
>>> list(deq) # 以列表形式展示deque的内容
['p', 'o', 'l', 'i', 'c', 'e']
>>> deq[0] # 获取最左侧的元素
'p'
>>> deq[-1] # 获取最右侧的元素
'e'
>>> list(reversed(deq)) # 以列表形式展示出倒序的deque的内容
['e', 'c', 'i', 'l', 'o', 'p']
>>> "l" in deq # 成员判断
True
>>> deq.extend("terrorist") # 添加多个元素
>>> deq
deque(['p', 'o', 'l', 'i', 'c', 'e', 't', 'e', 'r', 'r', 'o', 'r', 'i', 's', 't'])
>>> deq.rotate(1) # 往右反转
>>> deq
deque(['t', 'p', 'o', 'l', 'i', 'c', 'e', 't', 'e', 'r', 'r', 'o', 'r', 'i', 's'])
>>> deq.rotate(-1) # 往左反转
>>> deq
deque(['p', 'o', 'l', 'i', 'c', 'e', 't', 'e', 'r', 'r', 'o', 'r', 'i', 's', 't'])
>>> deque(reversed(deq)) # 以逆序新建一个deque
deque(['t', 's', 'i', 'r', 'o', 'r', 'r', 'e', 't', 'e', 'c', 'i', 'l', 'o', 'p'])
>>> deq.clear() # 清空deque
>>> deq.pop() # 不能在空的deque上pop
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
IndexError: pop from an empty deque
>>> deq.extendleft("terrorist") # 向左扩展
>>> deq
deque(['t', 's', 'i', 'r', 'o', 'r', 'r', 'e', 't'])

其他的应用:

1、限定长度的deque提供了Unix中tail命令相似的功能:

from collections import deque

def tail(filename, n=10):
return deque(open(filename, encoding="utf-8"), n) print(tail("collections.txt", 10))

2、使用deque维护一个列表(右侧添加元素,左侧删除元素)中窗口的平均值:

from collections import deque
import itertools def moving_average(iterable, n=3):
it = iter(iterable)
# 第一次只有两个元素,在右移的过程中,需要先删除最左端的元素,因此先在最左端加入0
deq = deque(itertools.islice(it, n-1))
deq.appendleft(0)
s = sum(deq)
for ele in it:
# 删除最左端的元素,再加上新元素
s += ele - deq.popleft()
# 右端添加新元素
deq.append(ele)
yield s / float(n) array = [40, 30, 50, 46, 39, 44]
for element in moving_average(array, n=3):
print(element)

3、rotate()方法提供了一种实现deque切片和删除的方式,例如:del deque[n]依赖于rotate方法的纯Python实现,如下:

from collections import deque

def delete_nth(deq, n):
# 从左侧开始,将n个元素翻转到右侧
deq.rotate(-n)
# 删除第n个元素
deq.popleft()
# 从右侧开始,将n个元素翻转到左侧
deq.rotate(n) d = deque("terrorist")
delete_nth(d, n=3)
print(d) # deque(['t', 'e', 'r', 'o', 'r', 'i', 's', 't'])

4、slice依赖于rotate方法的纯Python实现,如下:

from collections import deque

def slice_nth(deq, m, n):
# 从左侧开始,将前m个元素翻转到右侧
deq.rotate(-m)
i = m
slice_list = []
# 依次将[m, n]区间的元素出栈
while i < n:
item = deq.popleft()
slice_list.append(item)
i += 1 # 再将出栈的元素扩展到deque右侧
deq.extend(slice_list)
# 从右侧开始,将后n个元素翻转到左侧
deq.rotate(n)
return slice_list d = deque("abcdefgh")
print(slice_nth(d, 1, 5)) # ['b', 'c', 'd', 'e']

defaultdict

defaultdict是内置数据结构dict的一个子类,基本功能与dict一样,只是重写了一个方法__missing__(key)和增加了一个可写的对象变量default_factory。

>>> dir(dict)
['__class__', '__contains__', '__delattr__', '__delitem__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__getitem__', '__gt__', '__hash__', '__init__', '__iter__', '__le__', '__len__', '__lt__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__setitem__', '__sizeof__', '__str__', '__subclasshook__', 'clear', 'copy', 'fromkeys', 'get', 'items', 'keys', 'pop', 'popitem', 'setdefault', 'update', 'values']
>>> from collections import defaultdict
>>> dir(defaultdict)
['__class__', '__contains__', '__copy__', '__delattr__', '__delitem__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__getitem__', '__gt__', '__hash__', '__init__', '__iter__', '__le__', '__len__', '__lt__', '__missing__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__setitem__', '__sizeof__', '__str__', '__subclasshook__', 'clear', 'copy', 'default_factory', 'fromkeys', 'get', 'items', 'keys', 'pop', 'popitem', 'setdefault', 'update', 'values']

missing(key):

  • 1.如果default_factory属性为None,就报出以key作为遍历的KeyError异常;
  • 2.如果default_factory不为None,就会向给定的key提供一个默认值,这个值插入到字典中,并返回;
  • 3.如果default_factory报出异常,这个异常在传播时不会改变;
  • 4.这个方法是当要求的key不存在时,dict类中的__getitem__()方法所调用,无论它返回或者报异常,最终返回或报出给__getitem__();
  • 5.只有__getitem__()才能调用__missing__(),这意味着,如果get()起作用,如普通的字典,将会返回None作为默认值,而不是使用default_factory;

default_factory这个属性用于__missing__()方法,使用构造器中的第一个参数初始化;

使用list作为default_factory,很容易将一个key-value的序列转换为一个关于list的字典:

>>> from collections import defaultdict
>>> s_dict = [("red", 3), ("blue", 1), ("yellow", 2), ("blue", 4), ("yellow", 5)]
>>> d_dict = defaultdict(list)
>>> for key, value in s_dict: d_dict[key].append(value)
...
>>> d_dict.items()
dict_items([('yellow', [2, 5]), ('red', [3]), ('blue', [1, 4])])

当每个key第一次遇到时,还没有准备映射。首先会使用default_factory函数自动创建一个空的list,list.append()操作将value添加至新的list,当key再次遇到时,通过查表,返回对应这个key的list,list.append()会将新的value添加至list,这个技术要比dict.setdefault()要简单快速;

>>> e = {}
>>> for k, v in s_dict: e.setdefault(k, []).append(v)
...
>>> e.items()
dict_items([('yellow', [2, 5]), ('red', [3]), ('blue', [1, 4])])

设置default_factory为int,使得defaultdict可以用于计数:

>>> str = "intelligence"
>>> dict = defaultdict(int)
>>> for k in str: dict[k]+=1
...
>>> dict.items()
dict_items([('i', 2), ('t', 1), ('e', 3), ('g', 1), ('l', 2), ('n', 2), ('c', 1)])

当一个字母第一次遇到时,默认从default_factory中调用int()用于提供一个默认为0的计数,递增操作会增加每个字母的计数。函数int()经常返回0,是常量函数的一种特例,一种更快和更灵活的创建常量函数的方式是使用itertools.repeat(),可以提供任意常量值(不仅仅是0):

import itertools
from collections import defaultdict def constant_factory(value):
return lambda: next(itertools.repeat(value)) # Python3的.next方法已经不存在了所以使用next()方法,如果没有lambda会出错<不信你干掉它试试!!!> def_dict = defaultdict(constant_factory("<missing>"))
def_dict.update(name="Jack", action="speak")
print("%(name)s %(action)s to %(object)s" % def_dict) # Jack speak to <missing>

将default_factory设置为set,使得defaultdict可以建立一个关于set的字典:

>>> s = [('red', 1), ('blue', 2), ('red', 3), ('blue', 4), ('red', 1), ('blue',
... 4)]
>>> d = defaultdict(set)
>>> for k, v in s: d[k].add(v)
...
>>> d.items()
dict_items([('red', {1, 3}), ('blue', {2, 4})])

namedtuple

命名元组,意味着给元组中的每个位置赋予含义,意味着代码可读性更强,namedtuple可以在任何常规元素使用的地方使用,而且它可以通过名称来获取字段信息而不仅仅是通过位置索引。

# 基本用法
>>> from collections import namedtuple
>>> Point = namedtuple("Point", ["x", "y"])
>>> p = Point(11, y=22) # 实例化一个对象,可以使用位置或关键字
>>> p[0] + p[1] # 通过索引访问元组中的元素
33
>>> x, y = p # 分开,类似于常规的元组
>>> x, y
(11, 22)
>>> p.x + p.y # 通过名称来访问
33
>>> p # 可读的__repr__,通过name = value风格
Point(x=11, y=22)

namedtuple在给csv或者sqlite3返回的元组附上名称特别有用:

from collections import namedtuple
import csv EmployeeRecord = namedtuple('EmployeeRecord', 'name, age, title, department, paygrade') 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)

除了从tuple继承的方法之外,namedtuple还支持三种法发和一个属性,为了避免名称冲突,这些方法和属性以下划线开始。

somenamedtuple._make():从已有的序列或者可迭代对象中创建一个新的对象:

>>> t = [99, 88]
>>> Point._make(t)
Point(x=99, y=88)

somenamedtuple._asdict():返回一个OrderedDict,由名称到对应值建立映射:

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

somenamedtuple._replace():返回一个新的namedtuple对象,用新值替换指定名称中的值:

>>> p2 = p._replace(x=33)
>>> p2
Point(x=33, y=22)

somenamedtuple._fields:以字符串的构成的元组的形式返回namedtuple的名称,在自省或基于一个已存在的namedtuple中创建新的namedtuple时,非常有用:

>>> p._fields
('x', 'y')
>>> Color = namedtuple("Color", "red blue green")
>>> Pixel = namedtuple("Pixel", Point._fields + Color._fields)
>>> Pixel(11, 22, 127, 205,0)
Pixel(x=11, y=22, red=127, blue=205, green=0)

当名称存储在字符串中,可以使用getattr()函数进行检索:

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

使用**操作符,可以将一个字典转换成namedtuple:

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

由于namedtuple也是Python中的一个类,因此在子类中,它很容易添加或修改一些功能,如下是添加一个可计算名称和固定长度的输出格式;子类中的__slots__是一个空的元组,可以通过避免字典实例的创建来节约内存开销:

from collections import namedtuple

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

子类在增加、存储名称时,并不是非常有用;相反,可以容易地通过_fields属性来创建一个新的namedtuple:

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

默认可以通过_replace()来实现,以便于标准化一个原型实例:

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

枚举类型常量可以通过namedtuple来实现,更简单和有效的方式是通过简单的类声明:

Status = namedtuple('Status','open pending closed')._make(range(3))
print Status class Status:
open, pending, closed = range(3) print Status.open
print Status.pending
print Status.closed

运行输出结果为:

Status(open=0, pending=1, closed=2)
0
1
2

OrderedDict

  • OrderedDict类似于正常的字典,只是它记录了元素插入的顺序,当在有序的字典上迭代时,返回的元素就是它们第一次添加的顺序。
  • OrderedDict返回已给dict的子类,支持常规的dict方法,OrderedDict是一个记录元素首次插入顺序的字典,如果一个元素重写已经存在的元素,那么原始的插入位置保持不变,如果删除一个元素再重新插入,那么它就在末尾;
  • OrderedDict.popitem(last=True),popitem方法返回和删除一个(key, value)对,如果last=True,就以LIFO方式执行,否则以FIFO方式执行;
  • OrderedDict也支持反向迭代,例如:reversed();
  • OrderedDict对象之间的相等测试,例如:list(od1.items()) == list(od2.items()),是对顺序敏感的;OrderedDict和其他的映射对象(例如:常规的字典)之间的相等测试是顺序不敏感的,这就允许OrderedDict对象可以在使用常规字典的地方替换掉常规字典;
  • OrderedDict构造器和update()方法可以接收关键字参数,但是它们丢失了顺序,因为Python的函数调用机制是将一个无须的字典传入关键字变量;

一个有序的字典记住它的成员插入顺序,可以使用排序函数,将其变为排序的字典:

>>> org_dict = {"apple": 1, "tencent": 2, "alibaba": 3, "baidu": 4, "facebook": 5}
>>> from collctions import OrderedDict
>>> OrderedDict(sorted(org_dict.items(), key = lambda t: t[0])) # 根据key排序字典
OrderedDict([('alibaba', 3), ('apple', 1), ('baidu', 4), ('facebook', 5), ('tencent', 2)])
>>> OrderedDict(sorted(org_dict.items(), key = lambda t: t[1])) # 根据value排序字典
OrderedDict([('apple', 1), ('tencent', 2), ('alibaba', 3), ('baidu', 4), ('facebook', 5)])
>>> a = OrderedDict(sorted(org_dict.items(), key = lambda t: len(t[0]))) # 根据key的长度排序字典
>>> a
OrderedDict([('baidu', 4), ('apple', 1), ('tencent', 2), ('alibaba', 3), ('facebook', 5)])
>>> del a["facebook"]
>>> a
OrderedDict([('baidu', 4), ('apple', 1), ('tencent', 2), ('alibaba', 3)])
>>> a["facebook"]=9
>>> a
OrderedDict([('baidu', 4), ('apple', 1), ('tencent', 2), ('alibaba', 3), ('facebook', 9)])

当元素删除时,排好序的字典保持着排序的顺序;但是当新增元素时,就会被添加到末尾,就不能保持已排序;

创建一个有序的字典,可以记住最后插入的key的顺序,如果一个新的元素要重写已存在的元素,那么原始的插入位置就会变成末尾:

from collections import OrderedDict

class LastUpdateOrderedDict(OrderedDict):
def __setitem__(self, key, value):
if key in self:
del self[key]
OrderedDict.__setitem__(self, key, value) obje = LastUpdateOrderedDict()
obje["amazon"] = 12
obje["microsoft"] = -3
print("the first output: ", obje) # LastUpdateOrderedDict([('amazon', 12), ('microsoft', -3)])
obje["amazon"] = -1
print("the second output: ", obje) # LastUpdateOrderedDict([('microsoft', -3), ('amazon', -1)])

一个有序的字典可以和Counter类一起使用,counter对象就可以记住首次出现的顺序:

from collections import Counter, OrderedDict

class OrderedCounter(Counter, OrderedDict):
def __repr__(self):
return "%s(%r)" % (self.__class__.__name__, OrderedDict(self)) def __reduce__(self):
return self.__class__, (OrderedDict(self)) # 和OrderedDict一起使用的Counter对象
obj = OrderedCounter()
word_list = ['t', 'e', 'r', 'o', 'r', 'i', 's', 't']
for word in word_list:
obj[word] += 1
print("the new result is: ", obj) # OrderedCounter(OrderedDict([('t', 2), ('e', 1), ('r', 2), ('o', 1), ('i', 1), ('s', 1)])) # 普通的Counter对象
counter = Counter()
for ele in word_list:
counter[ele] += 1
print("the ordinary result is: ", counter) # Counter({'t': 2, 'r': 2, 'e': 1, 'o': 1, 'i': 1, 's': 1})

Python Collections详解的更多相关文章

  1. Python闭包详解

    Python闭包详解 1 快速预览 以下是一段简单的闭包代码示例: def foo(): m=3 n=5 def bar(): a=4 return m+n+a return bar >> ...

  2. [转] Python Traceback详解

    追莫名其妙的bugs利器-mark- 转自:https://www.jianshu.com/p/a8cb5375171a   Python Traceback详解   刚接触Python的时候,简单的 ...

  3. python 数据类型详解

    python数据类型详解 参考网址:http://www.cnblogs.com/linjiqin/p/3608541.html 目录1.字符串2.布尔类型3.整数4.浮点数5.数字6.列表7.元组8 ...

  4. Python 递归函数 详解

    Python 递归函数 详解   在函数内调用当前函数本身的函数就是递归函数   下面是一个递归函数的实例: 第一次接触递归函数的人,都会被它调用本身而搞得晕头转向,而且看上面的函数调用,得到的结果会 ...

  5. python线程详解

    #线程状态 #线程同步(锁)#多线程的优势在于可以同时运行多个任务,至少感觉起来是这样,但是当线程需要共享数据时,可能存在数据不同步的问题. #threading模块#常用方法:'''threadin ...

  6. python数据类型详解(全面)

    python数据类型详解 目录1.字符串2.布尔类型3.整数4.浮点数5.数字6.列表7.元组8.字典9.日期 1.字符串1.1.如何在Python中使用字符串a.使用单引号(')用单引号括起来表示字 ...

  7. python生成器详解

    1. 生成器 利用迭代器(迭代器详解python迭代器详解),我们可以在每次迭代获取数据(通过next()方法)时按照特定的规律进行生成.但是我们在实现一个迭代器时,关于当前迭代到的状态需要我们自己记 ...

  8. 转 python数据类型详解

    python数据类型详解 目录 1.字符串 2.布尔类型 3.整数 4.浮点数 5.数字 6.列表 7.元组 8.字典 9.日期 1.字符串 1.1.如何在Python中使用字符串 a.使用单引号(' ...

  9. python多线程详解

    目录 python多线程详解 一.线程介绍 什么是线程 为什么要使用多线程 二.线程实现 threading模块 自定义线程 守护线程 主线程等待子线程结束 多线程共享全局变量 互斥锁 递归锁 信号量 ...

随机推荐

  1. Fliptil_KEY

    Fliptil(fliptile.pas/c/cpp) [问题描述] 约翰知道,那些高智力又快乐的奶牛产奶量特别高.所以他做了一个翻瓦片的益智游戏来娱乐奶牛. 在一个M×N的骨架上,每一个格子里都有一 ...

  2. 每天看一片代码系列(四):layzr.js,处理图片懒加载的库

    所谓图片的懒加载,即只有当图片处于或者接近于当前视窗时才开始加载图片.该库的使用方法非常简单: var layzr = new Layzr({ attr: 'data-layzr', // attr和 ...

  3. STM32的System memory

    Main Flash memory 是STM32内置的Flash,一般我们使用JTAG或者SWD模式下载程序时,就是下载到这个里面,重启后也直接从这启动程序. System memory 从系统存储器 ...

  4. SpringBoot入门(四)——自动配置

    本文来自网易云社区 SpringBoot之所以能够快速构建项目,得益于它的2个新特性,一个是起步依赖前面已经介绍过,另外一个则是自动配置.起步依赖用于降低项目依赖的复杂度,自动配置负责减少人工配置的工 ...

  5. 手机APP测试如何进行兼容性测试?

    Android App兼容性测试是一个比较重要的App评价内容,实际上兼容性测试不仅仅和测试人员相关,在开发阶段就应当着重考虑,因为兼容性问题是除了实现App本身要求的功能后,必须要关注.而且至关重要 ...

  6. Appium(Python)测试混血App

    Hybrid App(混合模式移动应用)是指介于web-app.native-app这两者之间的app兼具Native App良好用户交互体验的优势和Web App跨平台开发的优势 HybridApp ...

  7. Jquery获取DOM绑定事件

    获取到当前正在执行的事件: $('#testDive').bind('click', function(event){alert('event: ' + event.type)}); 获取所有绑定事件 ...

  8. Consul 简介

    Consul包含很多组件,总体来数,Consul是一种服务发现和配置工具. 服务发现:一个客户端提供自己的服务,例如api服务,或者mysql服务,另一个客户端就可以利用Consul通过DNS或者ht ...

  9. leetcode-位1的个数(位与运算)

    位1的个数 编写一个函数,输入是一个无符号整数,返回其二进制表达式中数字位数为 ‘1’ 的个数(也被称为汉明重量). 示例 : 输入: 11 输出: 3 解释: 整数 11 的二进制表示为 00000 ...

  10. 213. String Compression【LintCode java】

    Description Implement a method to perform basic string compression using the counts of repeated char ...