每天学点Python之collections
collections模块在内置数据类型(dict、list、set、tuple)的基础上,提供了几个额外的数据类型:ChainMap、Counter、deque、defaultdict、namedtuple和OrderedDict等。
ChainMap
ChainMap是python3的新特性,它用来将多个map组成一个新的单元(原来的map结构仍然存在,类似于这些map被存在了一个list之中),这比新建一个map再将其他map用update加进来快得多。通过ChainMap可以来模拟嵌套的情景,而且多用于模板之中。
ChainMap支持普通map的所有操作,下面主要展示一下它的特性:
# 新建ChainMap及它的结构
In[2]: from collections import ChainMap
In[3]: m1 = {"color": "red", "user": "guest"}
In[4]: m2 = {"name": "drfish", "age": "18"}
In[5]: chainMap = ChainMap(m1, m2)
In[6]: print(chainMap.items())
ItemsView(ChainMap({"user": "guest", "color": "red"}, {"age": "18", "name": "drfish"}))
# 获取ChainMap中的元素
In[7]: print(chainMap.get("name"))
drfish
In[8]: print(chainMap.get("not"))
None
# 新增map
In[9]: m3 = {"data": "1-6"}
In[10]: chainMap = chainMap.new_child(m3)
In[11]: print(chainMap.items())
ItemsView(ChainMap({"data": "1-6"}, {"user": "guest", "color": "red"}, {"age": "18", "name": "drfish"}))
# parents属性
In[12]: print(chainMap.parents)
ChainMap({"user": "guest", "color": "red"}, {"age": "18", "name": "drfish"})
In[13]: print(chainMap.parents.parents)
ChainMap({"age": "18", "name": "drfish"})
In[14]: print(chainMap.parents.parents.parents)
ChainMap({})
# maps属性
In[15]: chainMap.maps
Out[15]:
[{"data": "1-6"},
{"color": "red", "user": "guest"},
{"age": "18", "name": "drfish"}]
- 可以传入多个map来初始化ChainMap,如参数为空,会自动加入一个空的map
- 获取的key如果不存在,则返回None
- 可以通过net_child()方法来新增map,新的map添加在前面
- parents属性返回除去第一个map后的ChainMap实例。
Counter
Counter是一个简单的计数器,使用起来非常方便。
初始化
Counter的初始化支持多种形式
In[18]: from collections import Counter
In[19]: c = Counter()
In[20]: c = Counter("hello")
In[21]: c = Counter({"a":3,"b":19})
In[22]: c = Counter(cats=2,dogs=1)
In[23]: c
Out[23]: Counter({"cats": 2, "dogs": 1})
Counter对于不存在的元素会返回0,如果要删除则需要用到del:
In[23]: c
Out[23]: Counter({"cats": 2, "dogs": 1})
In[24]: c["birds"]
Out[24]: 0
In[25]: del c["cats"]
In[26]: c
Out[26]: Counter({"dogs": 1})
elements()方法会返回所有的元素,有几个就返回几个;而most_common(n)方法可以返回数量排前n的元素及其数目:
In[29]: c = Counter(a=4, b=2, c=0, d=-2)
In[30]: list(c.elements())
Out[30]: ["a", "a", "a", "a", "b", "b"]
In[33]: c.most_common(1)
Out[33]: [("a", 3)]
不同的Counter之间还可以进行运算操作,subtract()方法进行减法后会影响调用方;&
操作表示取两个中数目小的一个(如果只在一个Counter中存在,则结果集中也没有),|
操作取数目大的。
In[46]: c = Counter(a=13, b=11)
In[47]: d = Counter(a=1, b=2)
In[48]: c.subtract(d)
In[49]: c
Out[49]: Counter({"a": 12, "b": 9})
In[51]: c+d
Out[51]: Counter({"a": 13, "b": 11})
In[52]: c-d
Out[52]: Counter({"a": 11, "b": 7})
In[54]: c&d
Out[54]: Counter({"a": 1, "b": 2})
In[55]: c|d
Out[55]: Counter({"a": 12, "b": 9})
注:进行运算操作时,如果数目结果为负的会被直接忽略
deque
deque就是一个双端队列,与list非常相似,不过可以同时在list的左边增删元素:
In[72]: dq = deque("abd")
In[73]: dq
Out[73]: deque(["a", "b", "d"])
In[74]: dq.append("1")
In[75]: dq.appendleft("-1")
In[76]: dq
Out[76]: deque(["-1", "a", "b", "d", "1"])
In[77]: dq.popleft()
Out[77]: "-1"
In[78]: dq.extendleft("hello")
In[79]: dq
Out[79]: deque(["o", "l", "l", "e", "h", "a", "b", "d", "1"])
注意extendleft(x)方法相当于先把x翻转后在添加到列表前端。然后比较好用的是rotate方法,可以使队列中的元素顺序前移或者后移:
In[82]: dq=deque("12345")
In[83]: dq
Out[83]: deque(["1", "2", "3", "4", "5"])
In[84]: dq.rotate(2)
In[85]: dq
Out[85]: deque(["4", "5", "1", "2", "3"])
In[86]: dq.rotate(-4)
In[87]: dq
Out[87]: deque(["3", "4", "5", "1", "2"])
defaultdict
defaultdict在普通的dict之上添加了默认工厂,使得键不存在时会自动生成相应类型的值:
In[88]: from collections import defaultdict
In[89]: d = defaultdict(int)
In[90]: d["a"]
Out[90]: 0
In[91]: d = defaultdict(dict)
In[92]: d["a"]
Out[92]: {}
如果初始化时没有加入默认工厂,则同样会抛出KeyError错误:
In[93]: d = defaultdict()
In[94]: d["a"]
Traceback (most recent call last):
d["a"]
KeyError: "a"
namedtuple
namedtuple是tuple的加强版本,最大的作用就是给tuple的每个位置的值设置了别名,增加了程序的可读性:
In[95]: from collections import namedtuple
In[96]: Point = namedtuple("Point",["x","y"])
In[97]: p = Point(1,2)
In[98]: p.x
Out[98]: 1
In[99]: p.y
Out[99]: 2
由于有了别名,相当于形成了一个字典,namedtuple可以转化为下面要介绍的OrderedDict,同时也可以通过字典转化而来:
In[100]: p._asdict()
Out[100]: OrderedDict([("x", 1), ("y", 2)])
In[101]: d = {"x":11,"y":22}
In[102]: Point(**d)
Out[102]: Point(x=11, y=22)
namedtuple也提供了获取所有域及修改相应值的方法:
In[108]: p
Out[108]: Point(x=1, y=2)
In[109]: p._fields
Out[109]: ("x", "y")
In[110]: p._replace(x=10)
Out[110]: Point(x=10, y=2)
OrderedDict
dict是无序,但有时我们希望字典是有序的,OrderedDict提供了这项服务,OrderedDict中的键值对是按照他们加入时的顺序存储的:
In[130]: fruit=(("apple", 4), ("banana", 3), ("orange", 2), ("pear", 1))
In[131]: d = dict(fruit)
In[132]: d
Out[132]: {"apple": 4, "banana": 3, "orange": 2, "pear": 1}
In[135]: d.popitem()
Out[135]: ("apple", 4)
In[136]: d.popitem()
Out[136]: ("orange", 2)
In[137]: d.popitem()
Out[137]: ("banana", 3)
In[138]: d.popitem()
Out[138]: ("pear", 1)
In[133]: od = OrderedDict(fruit)
In[134]: od
Out[134]: OrderedDict([("apple", 4), ("banana", 3), ("orange", 2), ("pear", 1)])
In[139]: od.popitem()
Out[139]: ("pear", 1)
In[140]: od.popitem()
Out[140]: ("orange", 2)
In[141]: od.popitem()
Out[141]: ("banana", 3)
In[142]: od.popitem()
Out[142]: ("apple", 4)
注:OrderedDict中popitem(last=True)函数如果last设置为False,则先加入的先弹出
可以通过move_to_end(key, last=True)函数来改变键值对的位置,True时移到末尾,False移到开头:
In[146]: od
Out[146]: OrderedDict([("apple", 4), ("banana", 3), ("orange", 2), ("pear", 1)])
In[147]: od.move_to_end("apple")
In[148]: od
Out[148]: OrderedDict([("banana", 3), ("orange", 2), ("pear", 1), ("apple", 4)])
In[149]: od.move_to_end("pear",False)
In[150]: od
Out[150]: OrderedDict([("pear", 1), ("banana", 3), ("orange", 2), ("apple", 4)])