Python库之collections:容器数据类型

本文通过对官方文档和很多博客的学习总结,详解了colleciotns库各个容器类的使用方法。这个模块实现了针对特定目标的容器,作为Python标准内建容器 dict , list , set , 和 tuple 的替代选择。
本文主要介绍了ChainMap,Counter,deque,defaultdict,namedtuple,OrderedDict。

ChainMap

class collections.ChainMap(*maps)

初识ChainMap

ChainMap类用于将多个字典(映射)快速链接到一起,作为一个单元(逻辑上的字典)处理。通常比创建一个新字典和多次调用update()实现相同操作要快很多。

例如我们有两个字典a = {'x': 1, 'z': 3}b = {'y': 2, 'z': 4},需要在这两个字典中执行查找更新等操作,就可以使用ChainMap类将这两个字典链接起来:

a = {
    
    'x': 1, 'z': 3}
b = {
    
    'y': 2, 'z': 4}
from collections import ChainMap
c = ChainMap(a, b)
print(c['x'], c['y'], c['z']) # 有重复的key时,第一次出现的value会被返回
c['z'] = 5 # 更新c中key对应的value,原来的字典a也会相应的变化。同样更新a的时候c也会变化,这里不再举例
print('c:', c)
print('a:', a)

结果输出:

1 2 3
c: ChainMap({
    
    'x': 1, 'z': 5}, {
    
    'y': 2, 'z': 4})
a: {
    
    'x': 1, 'z': 5}

而如果创建一个新字典并多次调用update()也可以实现相同操作:

a = {
    
    'x': 1, 'z': 3}
b = {
    
    'y': 2, 'z': 4}
c = b
c.update(a)
print(c['x'], c['y'], c['z'])
c['z'] = 5 # 更新c中key对应的value,原来的字典a和b都不会相应的变化。同样更新a和b后c不会变化,这里不再举例
print('c:', c)
print('a:', a)
print('b:', b)

结果输出:

1 2 3
c: {
    
    'y': 2, 'z': 5, 'x': 1}
a: {
    
    'x': 1, 'z': 3}
b: {
    
    'y': 2, 'z': 5, 'x': 1}

但这种做法速度更慢,并且更新操作不会在原来的字典和新字典之间相互传递。

ChainMap的实现

在构造函数中创建一个列表maps,用于按顺序存放传进来的字典,然后重新定义了常见的字典操作方法(如len(), keys(), values(), get()等)。

例如在上例中,maps就是maps = [{'x': 1, 'z': 3}, {'y': 2, 'z': 4}]:

a = {
    
    'x': 1, 'z': 3}
b = {
    
    'y': 2, 'z': 4}
from collections import ChainMap
c = ChainMap(a, b)
print('maps', c.maps)
print('len(c)', len(c))
print('list(c.keys())', list(c.keys()))
print('list(c.values())', list(c.values()))
print("c.get('x')", c.get('x'))
c['a'] = 0 # 向ChainMap添加新的键值对,会添加到maps[0]中
print(c)
print(a)

结果输出:

maps [{
    
    'x': 1, 'z': 3}, {
    
    'y': 2, 'z': 4}]
len(c) 3
list(c.keys()) ['y', 'z', 'x']
list(c.values()) [2, 3, 1]
c.get('x') 1
ChainMap({
    
    'x': 1, 'z': 3, 'a': 0}, {
    
    'y': 2, 'z': 4})
{
    
    'x': 1, 'z': 3, 'a': 0}

ChainMap的新功能

除了常见的字典操作,ChainMap还实现了以下方法:

方法 作用
copy() New ChainMap or subclass with a new copy of maps[0] and refs to maps[1:]
new_child(m=None) New ChainMap with a new map followed by all previous maps. If no map is provided, an empty dict is used.
parents() New ChainMap from maps[1:]
pipitem() 删除并返回maps[0]的最后一个键值对,如果maps[0]是空字典,raise KeyError
pip(key, *args) 从maps[0]中删除键值为key的键值对并返回其value,如果key不在maps[0]中,raise KeyError
clear() Clear maps[0],将maps[0]变成空列表

Counter

class collections.Counter([iterable-or-mapping])

初识Counter

Counter是字典dict的一个子类,用于对可哈希对象进行计数(技术值可为0和负数),例如:

from collections import Counter
test = ['red', 'blue', 'red', 'green', 'blue', 'blue']
cnt = Counter(test)
print(cnt)
print(cnt['red'])
print(cnt['blue'])
print(cnt['green'])

结果输出:

Counter({
    
    'blue': 3, 'red': 2, 'green': 1})
2
3
1

Counter的初始化方法

  1. 直接建立一个空的Counter对象,然后手动添加键值对,例如:
    from collections import Counter
    cnt = Counter() # 建立一个空的Counter对象
    # 手动添加
    for word in ['red', 'blue', 'red', 'green', 'blue', 'blue']:
        cnt[word] += 1
    print(cnt)
    
    输出为:
    Counter({
          
          'blue': 3, 'red': 2, 'green': 1})
    
  2. iterable建立一个Counter对象,例如:
    from collections import Counter
    cnt = Counter('gallahad')
    print(cnt)
    
    输出为:
    Counter({
          
          'a': 3, 'l': 2, 'g': 1, 'h': 1, 'd': 1})
    
  3. 从一个mapping(字典)建立一个Counter对象,例如:
    from collections import Counter
    cnt = Counter({
          
          'red': 4, 'blue': 2})
    print(cnt)
    
    输出为:
    Counter({
          
          'red': 4, 'blue': 2})
    
  4. 从关键字参数新建一个Counter对象,例如:
    from collections import Counter
    cnt = Counter(red=4, blue=2)
    print(cnt)
    
    输出为:
    Counter({
          
          'red': 4, 'blue': 2})
    

Counter的一些方法

方法 功能
elements() 返回一个迭代器,其中每个元素将重复出现技术值所指定次。元素会按首次出现的顺序返回。如果一个元素的技术值小于1,elements()会忽略它。
most_common([n]) 返回一个列表,其中包含n个最常见的元素及出现次数(tuple),按常见程度从高到低排序。如果n被被忽略或为None
substract([iterable-or-mapping) 从迭代对象或映射对象减去元素。
update([iterable-or-mapping) 从迭代对象计数元素或者从另一个映射对象(或计数器)添加元素。

例如:

from collections import Counter
cnt = Counter(a=4, b=2, c=0, d=-1)
print(list(cnt.elements()))
print(Counter('abracadabra').most_common(3))
tmp = Counter(a=1, b=2, c=3, d=4)
cnt.subtract(tmp)
print(cnt)
cnt.update(tmp)
print(cnt)

输出为:

['a', 'a', 'a', 'a', 'b', 'b']
[('a', 5), ('b', 2), ('r', 2)]
Counter({
    
    'a': 3, 'b': 0, 'c': -3, 'd': -5})
Counter({
    
    'a': 4, 'b': 2, 'c': 0, 'd': -1})

deque

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

初识deque

deque对象是一个双向队列,从iterable创建,如果iterable没有指定,新队列为空。

  • deque支持线性安全,内存搞笑添加(append)和弹出(pop),从两端都可以,两个方向的大概开销都是O(1)复杂度;
  • list也可以完成类似的操作,但是deque优化了定长操作和pop(0)insert(0, v)的开销,list开销为O(n),deque开销为O(1);
  • 如果maxlen没有指定或者为None,deque可以增长到任意长度,否则deque就限定到最大长度,一单限定长度的deque满了,当新项加入时,同样数量的项从另一端弹出。

deque的方法和属性

方法 功能
append(x) 添加x到右端
appendleft(x) 添加x到左端
clear() 移除所有元素,使其长度为0
copy() 创建一份浅拷贝
count(x) 计算deque中元素为x的个数
extend(iterable) 扩展deque的右侧,通过添加iterable中的元素
extendleft(iterable) 扩展deque的左侧,通过添加iterable中的元素。注意,左添加时iterable中的元素的顺序将被反过来添加
index(x[, start[, stop]]) 返回x在deque中的位置(在索引start之后,索引stop之前)。返回第一个匹配项,找不到返回ValueError
insert(i, x) 在位置i插入x
pop() 移去并且返回deque最右侧的元素,如果没有元素引发一个IndexError
popleft() 移去并且返回deque最左侧的元素,如果没有元素引发一个IndexError
remove(value) 移除找到的第一个value,如果没有引发一个ValueError
reverse() 将deque逆序排列。返回None
rotate(n=1) 向右循环移动n步。如果n为负数,就表示向左循环移动
maxlen deque的最大尺寸,没有限定则为None

除了以上操作,deque 还支持迭代、封存、len(d)reversed(d)copy.copy(d)copy.deepcopy(d)、成员检测运算符 in 以及下标引用例如通过 d[0] 访问首个元素等。 索引访问在两端的复杂度均为 O(1) 但在中间则会低至 O(n)。 如需快速随机访问,请改用列表。

defaultdict

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

defaultdict的定义和使用

defaultdict是内置dict类的子类。访问普通dict对象不存在的key时会返回KeyError报错,而defaultdict可以给不存在的key设置一个默认值,我们需要给参数default_factory(默认为None)赋值一个可调用对象(例如函数),当访问不存在的key时,返回这个可调用对象(不带参数的调用)的返回值,即为默认值,例如:

from collections import defaultdict

# 使用类型函数
print('--------test1----------')
test1 = defaultdict(list)  # 这里给defaultdict赋值函数list,函数list()返回值为空列表,故默认值为空列表
print(test1)
print(test1['a'])
print(test1)
test2 = defaultdict(int)  # 这里给defaultdict赋值函数int,函数int()返回值为0,故默认值为0
print(test2['a'])

# 使用自定义函数
print('--------test3----------')
def helper():
    return "Hello World!"
test3 = defaultdict(helper)
print(test3['a'])

# 使用匿名函数
print('--------test4----------')
test4 = defaultdict(lambda: 1)
print(test4['a'])

输出为:

--------test1----------
defaultdict(<class 'list'>, {
    
    })
[]
defaultdict(<class 'list'>, {
    
    'a': []})
0
--------test3----------
Hello World!
--------test4----------
1

namedtuple

collections.namedtuple(typename, field_names, *, rename=False, defaults=None, module=None)

初识namedtuple

namedtuple函数返回一个新的元组子类,名为typename。用法类似于C语言的struct。普通tuple对象类似于(item0, item1, ..., itemn),而namedtuple返回的元组子类则为每一个item都命名,既可以像普通tuple一样通过索引访问item,也可以通过名字访问item,例如定义一个点的坐标:

from collections import namedtuple

Point = namedtuple('Point', ['x', 'y', 'z'])
p = Point(11, y=22, z=33) # 两种方式传递参数都可以
print(p[0], p[1], p[2]) # 通过索引访问item
print(p.x, p.y, p.z) # 通过名字访问item

输出为:

11 22 33
11 22 33

namedtuple各个参数的意义和用法

typename
typename是子类的名字,设置为和等号前面的名字一样即可(不同好像也没啥影响)。
field_names
field_names包含各个item的名字,既可以是像['x', 'y', 'z']这种形式,也可以用空格或都好分隔开,例如'x y z''x, y, z'。fiel_names包含的名字可以是任意有效的Python标识符,除了下划线开头的以及关键字。
rename
如果rename为真,那么field_names中包含的无效名字会被自动替换为 下划线+位置索引。例如['abc', 'def', 'ghi', 'abc']中第二个(关键字)和第四个(重复出现)是无效名字,在rename为真时被转换为['abc', '_1', 'ghi', '_3']
defaults
defaults表示item的默认值,可以为Noneiterable。如果defaults指定的默认值个数少于feild_names指定的名字个数,那么默认值赋值给右边的名字。例如field_names为['x', 'y', 'z']而defaults为(1, 2),那么'y''z'的默认值分别为1和2,我们至少要指定一个'x'的值。

namedtuple的几个常用方法和属性

_make(iterable)
从给定的iterable创建一个新实例,例如上例中的Point可以如下创建:

t = [11, 22, 33]
p = Point._make(t)

_asdict()
将namedtuple转为字典(namedtuple相对于字典更轻量,占内存小)

print(type(p._asdict())) # 输出为:<class 'dict'>

_replace(**kwargs)
返回一个新namedtuple,并将指定域替换为新的值:

print(p._replace(x=44)) # 输出为:Point(x=44, y=22, z=33)

_fields
字符串元组,列出了各个item的名字。

print(p._fields) # 输出为:('x', 'y', 'z')

OrderedDict

class collections.OrderdDict([items])

以前python中的字典dict是无序的,即插入的顺序和输出的顺序不一定是相同的,因为它是按照hash来存储的。
而OrderedDict是有序的,插入的顺序和输出的顺序相同,并且两个OrderedDict对象,插入元素的顺序不同,即使元素相同,它们也不相等。
但目前dict已经具备了记忆插入顺序的能力,OrderedDict已经不那么重要了,一些细微差别这里不再介绍。

猜你喜欢

转载自blog.csdn.net/qyhaill/article/details/112093756
今日推荐