Python算法之collections模块学习

全文学习参考
这个模块实现了特定目标的容器,提供了Python标准内建容器 dict , list , set , 和 tuple 的替代选择。
模块中包括以下对象:
在这里插入图片描述

ChainMap对象

一个 ChainMap 类是为了将多个映射快速的链接到一起,这样它们就可以作为一个单元处理。它通常比创建一个新字典和多次调用 update() 要快很多。ChainMap支持常用的字典方法,另外包括maps属性,一个创建上下文的方法new_child以及一个存取收个映射的属性parents.

baseline = {'music': 'bach', 'art': 'rembrandt'}
adjustments = {'art': 'van gogh', 'opera': 'carmen'}
c = collections.ChainMap(baseline, adjustments)
print(c)  #等价于c.maps

>> ChainMap({'music': 'bach', 'art': 'rembrandt'}, {'art': 'van gogh', 'opera': 'carmen'})

c = c.new_child({'human':'dav'}) #等价于collections.ChainMap({'human':'dav'}, *c.maps)
print(c)
>>ChainMap({'human': 'dav'}, {'art': 'van gogh', 'opera': 'carmen'}, {'music': 'bach', 'art': 'rembrandt'})

print(c.parents) #属性返回一个新的 ChainMap 包含所有的当前实例的映射(除了第一个)。等价于 ChainMap(*c.maps[1:])
>>ChainMap({'art': 'van gogh', 'opera': 'carmen'}, {'music': 'bach', 'art': 'rembrandt'})

注意,一个 ChainMap() 的迭代顺序是通过从后往前扫描所有映射来确定的,对于常用的字典方法,ChainMap对象也是支持的:

baseline = {'music': 'bach'}
adjustments = {'art': 'van gogh', 'opera': 'carmen'}
per = {'art': 'wuwu'}
c = collections.ChainMap(adjustments,baseline,per)
c.update({"art":"beat"})  # 如果不指定,则会对找到的第一个字典进行修改,如果没找到,则会在第一个字典(maps[0])中进行更新。
print(c)   
>>ChainMap({'art': 'beat', 'opera': 'carmen'}, {'music': 'bach'}, {'art': 'wuwu'})

c.maps[2].update({"art":"hua"}) #指定字典则会对指定的字典进行修改
>>ChainMap({'art': 'beat', 'opera': 'carmen'}, {'music': 'bach'}, {'art': 'hua'})

用 ChainMap 类模拟嵌套上下文的例子:

baseline = {'music': 'bach'}
adjustments = {'art': 'van gogh', 'opera': 'carmen'}
c = collections.ChainMap(adjustments,baseline)
d = c.new_child({"qiji": "xiwang"})     # Create nested child context
e = c.new_child()     # Child of c, independent from d

print(d.maps[0])
>>{'qiji': 'xiwang'}

print(e.maps[0] )            # 类似于局部变量,输出当前上下文
>>{}

print(e.maps[-1] )           # 类似于全局变量,返回源上下文
>>{'music': 'bach'}


print(e.parents) 
>>ChainMap({'art': 'van gogh', 'opera': 'carmen'}, {'music': 'bach'})

print(d.parents)
>>ChainMap({'art': 'van gogh', 'opera': 'carmen'}, {'music': 'bach'})

同时也可以通过定义子类,来对ChainMap进行修改,实现深度写与删除。

class DeepChainMap(collections.ChainMap):
    'Variant of ChainMap that allows direct updates to inner scopes'

    def __setitem__(self, key, value):
        for mapping in self.maps:
            if key in mapping:
                mapping[key] = value
                return
        self.maps[0][key] = value

    def __delitem__(self, key):
        for mapping in self.maps:
            if key in mapping:
                del mapping[key]
                return
        raise KeyError(key)

d = DeepChainMap({'zebra': 'black'}, {'elephant': 'blue','snake' : 'green'  }, {'lion': 'yellow'})
d['lion'] = 'orange'         # 跟新已存在的
d['snake'] = 'red'           # 跟新已存在的
d['cat'] = 'black'  		# 加入不存在的,默认加入第一映射
del d['elephant']            #删除
print(d)
>>DeepChainMap({'zebra': 'black', 'cat': 'black'}, {'snake': 'red'}, {'lion': 'orange'})

Counter对象

一个计数器工具,提供快速和方便的计数。 Counter 是一个 dict 的子类,用于计数可哈希对象。

c = collections.Counter()                           # 空计数器
c = collections.Counter('gallahad')                 #迭代计算各个元素
c = collections.Counter({'red': 4, 'blue': 2})      #初始化映射
c = collections.Counter(cats=4, dogs=8)             #初始化关键字
>>
Counter()
Counter({'a': 3, 'l': 2, 'g': 1, 'h': 1, 'd': 1})
Counter({'red': 4, 'blue': 2})
Counter({'dogs': 8, 'cats': 4})

Counter对象有一个字典接口,如果引用的键没有任何记录,就返回一个0,而不是弹出一个 KeyError :

c = collections.Counter(['eggs', 'ham'])
print(c)
>>Counter({'eggs': 1, 'ham': 1})

print(c['hat'])
>> 0

del c['ham']             #删除一个计数,不是让他等于0,而是del命令
print(c)
>>Counter({'eggs': 1})

除去字典方法以外,还有三个其他的方法: elements() 返回一个迭代器,元素按首次出现顺序重复返回。most_common([n]) 返回一个列表,包含n个最常见的元素以及出现次数。subtract([iterable-or-mapping]) 从迭代对象或者映射对象中减去(更新)元素,输入输出可为负数。

c = collections.Counter(a=4, b=2, c=0, d=-2)
d = collections.Counter(a=1, b=2, c=3, d=4)

print(sorted(c.elements()))
>>['a', 'a', 'a', 'a', 'b', 'b']

print(c.most_common(2))
>>[('a', 4), ('b', 2)]

c.subtract(d)  #本地修改
print(c)
>>Counter({'a': 3, 'b': 0, 'c': -3, 'd': -6})

c = c.subtract(d)
print(c)
>> None

常见的使用方法:

c = collections.Counter(a=4, b=2,c = -2)
d = collections.Counter(a=1, b=2, c=3)
print(list(c))
>>['a', 'b', 'c']             

print(set(c))                  #返回一个集合
>>{'c', 'b', 'a'}
print(dict(c))
>>{'a': 4, 'b': 2, 'c': -2} 

print(c.items())
>>dict_items([('a', 4), ('b', 2), ('c', -2)])

print(collections.Counter(dict(c)))   #构造成对字典的counter
>>Counter({'a': 4, 'b': 2, 'c': -2})

print(c.most_common()[:-3:-1])     #[:-n-1:-1],返回倒数n个元素
>>[('c', -2), ('b', 2)]

#运算符操作
print(c+d)               #加法,结果保留正数
>>Counter({'a': 5, 'b': 4, 'c': 1})

print(c - d)       #减法,结果保留正数
>>Counter({'a': 3})

print(c & d)             #交集:min(c[x],d[x])
>>Counter({'b': 2, 'a': 1})

print(c | d)                  #并集:max(c[x],d[x])
>>Counter({'a': 4, 'c': 3, 'b': 2})

deque 对象

返回一个新的双向队列对象,从左到右初始化(用方法 append()) ,从 iterable (迭代对象) 数据创建。
支持如下方法:
append(x):添加x到右端。
appendleft(x):添加 x 到左端。
clear():移除所有元素,使其长度为0。
copy():创建一份浅拷贝。
count(x):计算 deque 中元素等于 x 的个数。

d = collections.deque('http')                 
for elem in d:                   # 迭代操作元素
    print(elem.upper())
>> H T T P

d.append('j')
>> deque(['h', 't', 't', 'p', 'j'])

d.appendleft('c')
>>deque(['c', 'h', 't', 't', 'p', 'j'])

d.pop()
>>j

d.popleft()
>>c

d[0]
>>h

##以下均初始化 d = collections.deque('http')   
list((d)) 
>> ['h', 't', 't', 'p']

list(reversed(d)) 
>> ['p', 't', 't', 'h']

d.extend('jkl')
>>deque(['h', 't', 't', 'p', 'j', 'k', 'l'])

d.rotate(1)   #向右旋转1
>>deque(['p', 'h', 't', 't'])

d.rotate(1)   #向左旋转1
>>deque(['t', 't', 'p', 'h'])

d.clear()    #清空队列
d.pop()        #清空后无法输出 报错

d.extendleft('abc')     #左边开始拓展
>>deque(['c', 'b', 'a', 'h', 't', 't', 'p'])

常用方法:

## 用来删除第n个元素  del d[n] 
def delete_nth(d, n):
    d.rotate(-n)
    d.popleft()
    d.rotate(n)

defaultdict 对象

返回一个新的类似字典的对象。 defaultdict 是内置 dict 类的子类。 它重载了一个方法并添加了一个可写的实例变量。
本对象包含一个名为 default_factory 的属性,构造时,第一个参数用于为该属性提供初始值,默认为 None。所有其他参数(包括关键字参数)都相当于传递给 dict 的构造函数。

使用 list 作为 default_factory,很轻松地将(键-值对组成的)序列转换为(键-列表组成的)字典:

s = [('yellow', 1), ('blue', 2), ('yellow', 3), ('blue', 4), ('red', 1)]
d = collections.defaultdict(list)
for k, v in s:
    d[k].append(v)
print(d.items())
>>dict_items([('yellow', [1, 3]), ('blue', [2, 4]), ('red', [1])])

#等价于以下dict操作,但是速度更快,更简单。
d = {}
for k, v in s:
    d.setdefault(k, []).append(v)

设置 default_factoryint,使 defaultdict 用于计数:

s = 'mississippi'
d = collections.defaultdict(int)
for k in s:
    d[k] += 1          #它会查询失败,则 default_factory 会调用 int() 来提供一个整数 0 作为默认值。
print(d)
>>defaultdict(<class 'int'>, {'m': 1, 'i': 4, 's': 4, 'p': 2})

函数 int() 总是返回 0,这是常数函数的特殊情况。一个更快和灵活的方法是使用 lambda 函数,可以提供任何常量值(不只是0):

def constant_factory(value):
    return lambda: value
d = collections.defaultdict(constant_factory('<missing>'))
d.update(name='John', action='ran')
print('%(name)s %(action)s to %(object)s' % d)
>>John ran to <missing>

设置 default_factoryset 使 defaultdict 用于构建 set 集合:

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

namedtuple() 命名元组的工厂函数

命名元组赋予每个位置一个含义,提供可读性和自文档性。它们可以用于任何普通元组,并添加了通过名字获取值的能力,通过索引值也是可以的。

Point = collections.namedtuple('Point', ['x', 'y'])
p = Point(11,12)
print(p)
>>Point(x=11, y=12)

print(p[0] + p[1])
>>23

x,y = p  #x=11,y=12                 #像常规元组一样解包
print(p.x + p.y)                      #字段也可以通过名称访问  
>>23

print(p)
>>Point(x=11, y=12)

_make() 方法构建新的实例,_asdict() 方法返回一个字典映射。

Point = collections.namedtuple('Point', ['x', 'y'])
t = [11, 22]
p = Point._make(t)
print(p)
>>Point(x=11, y=22)

p = Point._asdict(p)
print(p)
>>{'x': 11, 'y': 22}

_replace(kwargs) 返回一个新的命名元组实例,并将指定域替换为新的值,_fields属性返回元组字段名,_field_defaults**字典将字段名称映射到默认值。:

Point = collections.namedtuple('Point', ['x', 'y'])
p = Point(x = 11,y = 22)
p = p._replace(x = 33)
print(p)
>>Point(x=33, y=22)

print(p._fields)
>>('x', 'y')
Point = collections.namedtuple('Point', ['x', 'y'],defaults=[0,0])
p = Point(x=11,y=23)
print(p._field_defaults)
>>{'x': 0, 'y': 0}

OrderedDict 对象

有序词典就像常规词典一样,但有一些与排序操作相关的额外功能。
class collections.OrderedDict([items]) 返回一个 dict 子类的实例,它具有专门用于重新排列字典顺序的方法。

popitem(last=True) 移除并返回一个 (key, value) 键值对, 如果 last 值为真,则按 LIFO 后进先出的顺序返回键值对,否则就按 FIFO 先进先出的顺序返回键值对。
move_to_end(key, last=True) 将现有 key 移动到有序字典的任一端。 如果 last 为真值(默认)则将元素移至末尾;如果 last 为假值则将元素移至开头。如果 key 不存在则会触发 KeyError:

d = collections.OrderedDict.fromkeys('abcde')
print(d)
>>OrderedDict([('a', None), ('b', None), ('c', None), ('d', None), ('e', None)])

d.move_to_end('b')
print(''.join(d.keys()))
>>acdeb

print(d.popitem())
>>('b', None)

常见用法:
创建记住键值 最后 插入顺序的有序字典变体很简单。 如果新条目覆盖现有条目,则原始插入位置将更改并移至末尾:

class LastUpdatedOrderedDict(collections.OrderedDict):
    'Store items in the order the keys were last added'

    def __setitem__(self, key, value):
        super().__setitem__(key, value)
        self.move_to_end(key)
d = LastUpdatedOrderedDict.fromkeys('abcdeda')
print(d)
>>LastUpdatedOrderedDict([('b', None), ('c', None), ('e', None), ('d', None), ('a', None)])
  • 2
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值