namedtuple
OrderedDict defaultdict
deque
Counter
ChainMap
collections中的高级数据结构
先来看看collections模块中包含的数据结构,查看源码如下:
__all__ = ['deque', 'defaultdict', 'namedtuple', 'UserDict', 'UserList',
'UserString', 'Counter', 'OrderedDict', 'ChainMap']
这里主要介绍其中的数据结构:deque, defaultdict, namedtuple, Counter, OrderedDict和ChainMap。且其中更常用且需要重点掌握的是namedtuple,defaultdict。
回顾tuple
- 定义及初始化
- tuple(),()
- tuple(iterable)如tuple(range(3))
- t=(1,)
- t=(1,)*5
- 方法
- t[index]
- t.index(value[,start,[stop]])
- t.count(value)
- len(t)
- 拆包
user_tuple = ('jerry', 12)
name, age = user_tuple
print(name, age)
user_tuple = ('jerry', 12, 'male')
name, *other_info = user_tuple
print(name, other_info)
namedtuple
命名元组namedtuple(typename, field_names, *, rename=False, defaults=None, module=None)
- 返回一个元组的子类,并定义了字段
- field_names可以是空白符或者逗号分割的字段的字符串,可以是字段的列表
# 回顾类实例的属性访问
class User:
def __init__(self, name, age, *other_info):
self.name = name
self.age = age
self.other_info = other_info
j = User('jerry', 12, 'male')
print(j, j.age, j.name, j.other_info)
# 用命名元组实现
from collections import namedtuple
# User = namedtuple('User', ('name', 'age'))
# User = namedtuple('User', 'name age')
# User = namedtuple('User', 'name,age')
User = namedtuple('User', ['name', 'age']) # 上面几种方式都可以,一般常用列表
print(User, type(User))
j = User('jerry', 28) # 或j = User(name='jerry', age=28)
print(j, type(j))
print(j.name, j.age)
# j.age = 30 # AttributeError: can't set attribute # namedtuple是tuple的子类,tuple的特性,不可变
name, age = j # 拆包
print(name, age)
# namedtuple是tuple的子类,因此可使用tuple相关的方法
print(1, j.__class__, j.__class__.__base__)
print(j.index(28))
print(len(j))
- namedtuple实例化
# namedtuple实例化
User = namedtuple('User', ['name', 'age', 'sex'])
j1 = User('jerry', 28, 'male') # 方式1
j2 = User(name='jerry', age=28, sex='male') # 方式2
user_tuple = ('jerry', 28, 'male')
user_dict = {
'name': 'jerry',
'age': 28,
'sex': 'male'
}
j3 = User(*user_tuple) # 方式3
j4 = User(**user_dict) # 方式4
print(j1, j2, j3, j4, sep='\n')
User = namedtuple('User', ['name', 'age', 'sex', 'height'])
j5 = User(*user_tuple, height=170)
print(j5)
_make(cls, iterable)
和_asdict(self)
User = namedtuple('User', ['name', 'age', 'sex'])
user_dict = {
'name': 'jerry',
'age': 28,
'sex': 'male'
}
user_tuple = ('jerry', 28, 'male')
j = User('jerry', 28, 'male')
print(User._make(user_dict.values()))
print(User._make(user_tuple))
print(j._asdict(), type(j._asdict()))
_make()
可接受一个可迭代对象作为参数可不用拆包;_asdict()
是类的实例方法,可将返回一个字典
回顾dict
dict相关知识可参看我之前的博文python学习笔记:字典dict
几点额外补充:
- py3.6字典是有序的
- py3.8可使用reversed来反转dict
- py3.9新增合并运算
jerry_info = {'name': 'jerry', 'age': 28}
jerry_other_info = {'height': 170, 'sex': 'male'}
jerry_extra = reversed(jerry_other_info)
print(type(jerry_extra))
# print(next(jerry_extra))
print(list(jerry_extra))
jerry_other_info = dict(reversed(jerry_other_info.items()))
print(jerry_other_info)
jerry_about = jerry_info | jerry_other_info # 执行合并运算时,如果字典包含相同的 key, 运算结果将采用第二个字典的键值对
print(jerry_about)
jerry_info |= jerry_other_info
print(jerry_info)
# |= 操作符另外一个功能是使用一个可迭代对象的键值对更新字典
update_info = ((i, i ** 2) for i in range(3))
jerry_info |= update_info
print(jerry_info)
OrderedDict 和 defaultdict
- OrderedDict
class OrderedDict(dict):
- py3.6 版本字典就是有序的,如为兼容老版本py,可使用 OrderedDict
- 且OrderedDict还额外提供popitem(self, last=True)和move_to_end(self, key, last=True)更方便的操作
- defaultdict([default_factory[, ...]])
- 第一个参数 default_factory,缺省是 None,它提供一个初始化函数。当 key 不存在的时候,会调用这个工厂函数来生成 key 对应的 value(生成的是value)
from collections import OrderedDict
user_dict = {'name': 'jerry', 'age': 28, 'sex': 'male', 'height': 170, 'salary': 999999}
user_ordered_dict = OrderedDict(user_dict)
print(1, user_ordered_dict)
print(2, user_ordered_dict['name'])
print(3, user_ordered_dict.pop('sex'))
print(4, user_ordered_dict)
print(5, user_ordered_dict.popitem())
print(6, user_ordered_dict)
user_ordered_dict.move_to_end('age')
print(7, user_ordered_dict)
先看一个例子:统计一个list中的不同元素的个数
# 常规做法
num_list = [1, 2, 2, 'a', 1, 'b', 'a']
num_dict = {}
for i in num_list:
if i not in num_dict:
num_dict[i] = 1
else:
num_dict[i] += 1
print(num_dict)
# 利用dict的setdefault,避免使用太多的if判断
num_list = [1, 2, 2, 'a', 1, 'b', 'a']
num_dict = {}
for i in num_list:
num_dict.setdefault(i, 0)
num_dict[i] += 1
print(num_dict)
- 可不可以进一步简化?
# 使用defaultdict来简化
from collections import defaultdict
num_list = [1, 2, 2, 'a', 1, 'b', 'a']
num_dict = defaultdict(int)
# print(num_dict) # defaultdict(<class 'int'>, {})
for i in num_list:
num_dict[i] += 1
print(num_dict) # defaultdict(<class 'int'>, {1: 2, 2: 2, 'a': 2, 'b': 1})
- 进一步理解defaultdict,例子:生成固定结构的字典
from collections import defaultdict
def gen():
return {
'name': 'jerry',
'age': 28
}
user_dict = defaultdict(gen)
print(user_dict['base_info'])
deque
- 双端队列,可在队列两头操作
- 用c实现,比list相关方法性能更优
from collections import deque
l1 = [28, 170, 'male']
l2 = ['tester']
l3 = ['No.1']
l_deque = deque(l1)
l_deque.append('master')
l_deque.appendleft('jerry')
print(l_deque)
l_deque.extend(l2)
print(l_deque)
l_deque.extendleft(l3)
print(l_deque)
l_deque.pop()
print(l_deque)
l_deque.popleft()
print(l_deque)
更多的方法可参考源码
Counter
Dict subclass for counting hashable items. Sometimes called a bag or multiset. Elements are stored as dictionary keys and their counts are stored as dictionary values.
class Counter(dict): def __init__(self, iterable=None, /, **kwds):
主要用于计数- dict子类,可使用dict相关操作
most_common(n)
:返回top n 的元素数据,从大到小
from collections import Counter
s1 = 'abcdeabcdabcaba'
s2 = ['a', '1', '2', 'a', '1', 'b']
# c1 Counter({'a': 4, 'b': 2}) # a new counter from a mapping
# c2 = Counter(a=4, b=2) # a new counter from keyword args
s1_count = Counter(s1) # 从多到少排序
s2_count = Counter(s2)
print(1, s1_count, s2_count, type(s1_count))
# Counter为dict的子类
print(2, s1_count['a']) # 字典访问
s1_count.update(s2_count) # 字典操作
print(3, s1_count)
print(4, s1_count.elements(), s1_count.values(), s1_count.keys())
print(5, sorted(s1_count)) # 去重后的list
print(6, s1_count.most_common(2)) # top2的数据
ChainMap
将多个映射快速的链接到一起,这样它们就可以作为一个单元处理
# 例1:ChainMap的属性和方法
from collections import ChainMap
d1 = {'name': 'jerry', 'age': 28}
d2 = {'name': 'tom', 'sex': 'male'} # 与d1有相同的k-v
d = ChainMap(d1, d2)
print(1, d)
for k, v in d.items():
print(k, v) # d1,d2若有相同的k-v,则值打印d1中的
print(2, d.maps) # maps属性,一个可以更新的映射列表
d.maps[0]['age'] = 30
print(3, d, d1) # d1也被修改
print(4, d.new_child())
d3 = d.new_child({'edu': 'master'})
print(5, d, d3) # d不变
print(6, d3.parents)
d['name'] = 'tina' # 对于字典的更新或删除操作总是影响的是列表中第一个字典
print(7, d)
d1['name'] = 'lily' # 一个 ChainMap 通过引用合并底层映射。 所以,如果一个底层映射更新了,这些更改会反映到 ChainMap
print(8, d)
# 例2:假设你必须在两个字典中执行查找操作 (比如先从 a 中找,如果找不到再在 b中找)
a = {'x': 8, 'z': 7}
b = {'y': 2, 'z': 4}
c = ChainMap(a, b)
print(c, c.get('x'), c.get('y'), c.get('z'))
其他更多方法或属性可参看源码