collections是Python标准库中的一个内置模块,它提供了一些额外的数据结构类型,用于增强Python基础类型如列表(list)、元组(tuple)和字典(dict)等。以下是对collections模块中主要数据结构类的概述:
- namedtuple:命名元组,创建一个带有名称的tuple,并且可以通过名称访问元素。
- deque:双端队列,可以在两端高效地执行插入和删除操作。
- Counter:计数器,用于计算可迭代对象中元素的出现次数。
- defaultdict:默认字典,类似于普通字典,但是在访问不存在的键时会返回一个默认值。
- OrderedDict:有序字典,可以设置字典键值的顺序。
- ChainMap:将多个字典或映射组合在一起的类。
- UserList:列表的包装器类,用于创建自定义列表。
- UserString:字符串的包装器类,用于创建自定义字符串。
- UserDict:字典的包装器类,用于创建自定义字典。
本文主要介绍这些数据类的基础使用方法,以更好地利用Python的collections模块来处理不同类型的数据。关于collections模块更详细的使用介绍可以参考Python官方文档:python-collections。
1 namedtuple
namedtuple类似于元组(tuple),但是可以通过为每个元素指定名称,从而实现使用元素字段名来引用其元素,而不仅仅依赖于位置索引。
以下代码展示了namedtuple的使用
| from collections import namedtuple |
| |
| |
| Person = namedtuple('Person', ['name', 'age']) |
| |
| |
| |
| person1 = Person('Alice', 17) |
| |
| |
| print(person1.name) |
| print(person1.age) |
| |
| |
| print(person1[0]) |
| print(person1[1]) |
| |
| |
| |
| |
| |
| person2 = person1._replace(name='Bob') |
| print(person2) |
| |
| print(person2._fields) |
| Alice |
| 17 |
| Alice |
| 17 |
| Person(name='Bob', age=17) |
| ('name', 'age') |
从以上代码可以看到namedtuple和Python字典类型有一些相似之处,但它们在实现和使用方式上存在很大的差异,需要根据具体的需求和情况选择合适的数据类型。如果需要保持字段的顺序、提高访问速度和内存效率,可以选择namedtuple。而如果需要动态地添加、删除和修改键值对,并且需要使用字典提供的更多内置方法和功能,那么字典类型可能更适合。相比字典类型,namedtuple优劣如下:
namedtuple优势:
- 访问速度快:
namedtuple
内部使用整数索引访问字段,因此比字典更高效。
- 内存效率高:
namedtuple
采用紧凑的内存布局,相比字典更节省内存。
- 字段顺序固定:
namedtuple
定义时可以指定字段的顺序,并且不可变。这对于涉及字段顺序的操作非常有用。
namedtuple劣势:
- 不可变性:
namedtuple
的字段是不可变的,一旦创建就不能修改。而字典可以动态地添加、删除和修改键值对。
- 灵活性较差:字典提供了更多的内置方法和功能,例如迭代、查找、更新等。
namedtuple
相对简化,没有这些额外的功能。
以下代码展示了namedtuple和普通字典占用空间大小的效果对比:
| import random |
| import sys |
| from collections import namedtuple |
| |
| |
| person_dict = {'age': 32, 'name': 'John Doe'} |
| print('person_dict占用的空间大小:', sys.getsizeof(person_dict)) |
| |
| |
| Person = namedtuple('Person', ['age', 'name']) |
| person_tuple = Person(**person_dict) |
| print('person_tuple占用的空间大小:', sys.getsizeof(person_tuple)) |
| person_dict占用的空间大小: 248 |
| person_tuple占用的空间大小: 72 |
2 deque
deque(双端队列)是一种具有队列和栈性质的数据结构,它允许从两端快速地添加和删除元素。deque类似列表list,但deque在插入和删除元素时具有更好的性能,尤其是在操作频繁的情况下。以下代码展示了deque的使用。
| from collections import deque |
| |
| |
| my_deque = deque() |
| |
| my_deque = deque([1, 2, 3]) |
| |
| my_deque = deque([1, 2, 3], maxlen=5) |
| |
| |
| my_deque.append(1) |
| |
| my_deque.appendleft(2) |
| |
| |
| right_element = my_deque.pop() |
| |
| left_element = my_deque.popleft() |
| |
| |
| print(my_deque) |
| |
| print(my_deque[0]) |
| |
| |
| |
| print(list(my_deque)[:-1]) |
| deque([1, 2, 3], maxlen=5) |
| 1 |
| [1, 2] |
deque也支持基于字符串或列表来添加元素,如下所示:
| from collections import deque |
| |
| |
| my_deque = deque() |
| |
| |
| my_deque.extend([1, 2, 3]) |
| print(my_deque) |
| |
| |
| my_deque.extendleft("Hello") |
| print(my_deque) |
| deque([1, 2, 3]) |
| deque(['o', 'l', 'l', 'e', 'H', 1, 2, 3]) |
deque一些常用函数操作如下所示:
| from collections import deque |
| |
| |
| my_deque = deque() |
| |
| my_deque.extendleft("Hello") |
| |
| |
| print(len(my_deque)) |
| |
| print(my_deque.count("l")) |
| |
| |
| my_deque.insert(0, "123") |
| print(my_deque) |
| |
| |
| |
| my_deque.rotate(2) |
| print(my_deque) |
| |
| |
| my_deque.reverse() |
| print(my_deque) |
| |
| |
| my_deque.clear() |
| print(my_deque) |
| 5 |
| 2 |
| deque(['123', 'o', 'l', 'l', 'e', 'H']) |
| deque(['e', 'H', '123', 'o', 'l', 'l']) |
| deque(['l', 'l', 'o', '123', 'H', 'e']) |
| deque([]) |
3 Counter
Counter用于计算可迭代对象中元素的出现次数,这些可迭代对象可以是列表、字符串、元组等。
以下代码展示了Counter的使用。
| from collections import Counter |
| |
| |
| print(Counter(['a','c','d','d','b','c','a'])) |
| |
| |
| print(Counter('aabbacdd')) |
| |
| |
| string_count = Counter('aabbacdd') |
| |
| |
| for num, count in dict(string_count).items(): |
| print(num, count) |
| |
| |
| for item in string_count.items(): |
| print(item) |
| Counter({'a': 2, 'c': 2, 'd': 2, 'b': 1}) |
| Counter({'a': 3, 'b': 2, 'd': 2, 'c': 1}) |
| a 3 |
| b 2 |
| c 1 |
| d 2 |
| ('a', 3) |
| ('b', 2) |
| ('c', 1) |
| ('d', 2) |
若分别计算字符串中词的出现次数和字符的出现次数,代码如下:
| from collections import Counter |
| |
| line = '你好 世界 你好 !' |
| |
| |
| list_of_words = line.split() |
| |
| word_count = Counter(list_of_words) |
| |
| print(word_count) |
| |
| line = '你好 世界 你好 !' |
| |
| |
| string_count = Counter(line) |
| |
| print(string_count) |
| Counter({'你好': 2, '世界': 1, '!': 1}) |
| Counter({' ': 3, '你': 2, '好': 2, '世': 1, '界': 1, '!': 1}) |
Counter相关功能函数d的使用如下所示:
| from collections import Counter |
| |
| |
| word_count = Counter(['a', 'c', 'd', 'd', 'b', 'c', 'a']) |
| |
| |
| print(word_count.most_common(2)) |
| |
| print(word_count.most_common()) |
| |
| |
| print(word_count.elements()) |
| |
| print(list(word_count.elements())) |
| |
| print(sorted(word_count.elements())) |
| |
| print(sorted(word_count)) |
| |
| print(word_count.keys()) |
| |
| print(word_count.values()) |
| [('a', 2), ('c', 2)] |
| [('a', 2), ('c', 2), ('d', 2), ('b', 1)] |
| .chain object at 0x7efe4809fcd0> |
| ['a', 'a', 'c', 'c', 'd', 'd', 'b'] |
| ['a', 'a', 'b', 'c', 'c', 'd', 'd'] |
| ['a', 'b', 'c', 'd'] |
| dict_keys(['a', 'c', 'd', 'b']) |
| dict_values([2, 2, 2, 1]) |
对Counter中单个元素的操作,代码如下:
| from collections import Counter |
| |
| |
| word_count = Counter(['a', 'c', 'd', 'd', 'b', 'c', 'a']) |
| |
| |
| print(word_count["c"]) |
| |
| |
| word_count.update(['b', 'e']) |
| print(word_count) |
| |
| |
| del word_count["e"] |
| print(word_count) |
| |
| |
| word_count['f'] += 3 |
| print(word_count) |
| |
| |
| print(Counter('abc') & Counter('bde')) |
| |
| print(Counter('abc') | Counter('bde')) |
| 2 |
| Counter({'a': 2, 'c': 2, 'd': 2, 'b': 2, 'e': 1}) |
| Counter({'a': 2, 'c': 2, 'd': 2, 'b': 2}) |
| Counter({'f': 3, 'a': 2, 'c': 2, 'd': 2, 'b': 2}) |
| Counter({'b': 1}) |
| Counter({'a': 1, 'b': 1, 'c': 1, 'd': 1, 'e': 1}) |
4 defaultdict、OrderedDict
4.1 defaultdict
defaultdict是Python标准库collections模块中的一个类,它是dict类的一个子类。defaultdict的作用是创建一个字典,当访问字典中不存在的键时,不会抛出KeyError异常,而是返回一个默认值。defaultdict用法如下:
| from collections import defaultdict |
| |
| |
| d = defaultdict(int) |
| |
| |
| print(d['a']) |
| |
| |
| d['b'] = 2 |
| print(d) |
| |
| |
| d['c'] += 1 |
| print(d) |
| 0 |
| defaultdict( 'int'>, {'a': 0, 'b': 2}) |
| defaultdict( 'int'>, {'a': 0, 'b': 2, 'c': 1}) |
4.2 OrderedDict
OrderedDict的使用方法与普通字典dict类似,唯一的区别是它可以设置元素的顺序。
| from collections import OrderedDict |
| |
| |
| order_dict = OrderedDict() |
| |
| |
| order_dict['apple'] = 3 |
| order_dict['banana'] = 2 |
| order_dict['orange'] = 5 |
| print(order_dict) |
| |
| |
| fruits = {'banana': 2, 'apple': 3, 'orange': 5} |
| |
| |
| order_dict = OrderedDict(sorted(fruits.items(), key=lambda x: x[0])) |
| print(order_dict) |
| |
| |
| order_dict = OrderedDict(sorted(fruits.items(), key=lambda x: x[1])) |
| print(order_dict) |
| |
| |
| order_dict = OrderedDict(sorted(fruits.items(), key=lambda x: len(x[0]))) |
| print(order_dict) |
| OrderedDict([('apple', 3), ('banana', 2), ('orange', 5)]) |
| OrderedDict([('apple', 3), ('banana', 2), ('orange', 5)]) |
| OrderedDict([('banana', 2), ('apple', 3), ('orange', 5)]) |
| OrderedDict([('apple', 3), ('banana', 2), ('orange', 5)]) |
5 ChainMap
ChainMap用于方便地合并多个字典或映射对象,使它们作为一个整体进行操作。具体使用方法如下:
| from collections import ChainMap |
| |
| employee1 = {'John': '001', 'Mary': '002', 'David': '003'} |
| employee2 = {'Lisa': '004', 'Michael': '005', 'Sarah': '006'} |
| employee3 = {'Peter': '007', 'Emily': '008', 'Ryan': '009'} |
| |
| |
| combined_employees = ChainMap(employee1, employee2, employee3) |
| |
| |
| print(combined_employees.maps) |
| |
| |
| print(list(combined_employees.keys())) |
| |
| |
| print(list(combined_employees.values())) |
| [{'John': '001', 'Mary': '002', 'David': '003'}, {'Lisa': '004', 'Michael': '005', 'Sarah': '006'}, {'Peter': '007', 'Emily': '008', 'Ryan': '009'}] |
| ['Peter', 'Emily', 'Ryan', 'Lisa', 'Michael', 'Sarah', 'John', 'Mary', 'David'] |
| ['007', '008', '009', '004', '005', '006', '001', '002', '003'] |
如果要合并的对象中出现键值重合,使用ChainMap时将按照添加顺序,以最先添加的字典为准。在这种情况下,相同的键值经过合并后,会取第一个字典中的值作为重复键的值。具体示例如下:
| from collections import ChainMap |
| |
| |
| employee1 = {'John': '001', 'Mary': '002'} |
| employee2 = {'Lisa': '004', 'John': '005'} |
| |
| |
| combined_employees = ChainMap(employee1, employee2) |
| |
| print(combined_employees.maps) |
| print(list(combined_employees.keys())) |
| print(list(combined_employees.values())) |
| [{'John': '001', 'Mary': '002'}, {'Lisa': '004', 'John': '005'}] |
| ['Lisa', 'John', 'Mary'] |
| ['004', '001', '002'] |
在创建ChainMap对象后,也可以为其添加新的字典类型子项。
| from collections import ChainMap |
| |
| employee1 = {'John': '001', 'Mary': '002', 'David': '003'} |
| employee2 = {'Mary': '004', 'Michael': '005', 'Sarah': '006'} |
| employee3 = {'Peter': '007', 'Emily': '008', 'Ryan': '009'} |
| |
| combined_employees = ChainMap(employee1, employee2, employee3) |
| |
| |
| employee4 = {'Jack': '010', 'Halr': '011'} |
| |
| combined_employees = combined_employees.new_child(employee4) |
| |
| print(combined_employees) |
| ChainMap({'Jack': '010', 'Halr': '011'}, {'John': '001', 'Mary': '002', 'David': '003'}, {'Mary': '004', 'Michael': '005', 'Sarah': '006'}, {'Peter': '007', 'Emily': '008', 'Ryan': '009'}) |
6 UserList、UserString、UserDict
6.1 UserList
UserList是list的包装类,用于创建一个自定义的列表类。如下所示,UserList可以像普通list一样操作:
| from collections import UserList |
| |
| |
| my_list = [13, 4, 1, 5, 7] |
| |
| |
| |
| user_list = UserList(my_list) |
| |
| |
| print(user_list) |
| |
| |
| print(user_list.data) |
| |
| print(user_list[:-1]) |
| [13, 4, 1, 5, 7] |
| [13, 4, 1, 5, 7] |
| [13, 4, 1, 5] |
UserList的好处在于可以创建一个继承自UserList的子类,以便自定义列表的各个方法。下面是重写了append方法的简单示例:
| from collections import UserList |
| |
| class MyList(UserList): |
| def __init__(self, initialdata=None): |
| super().__init__(initialdata) |
| |
| def append(self, item): |
| |
| print("Appending", item) |
| super().append(item) |
| |
| |
| |
| my_list = MyList([1, 2, 3]) |
| my_list.append(4) |
| print(my_list) |
6.2 UserString
UserString用于创建自定义字符串类。通过继承UserString类,可以创建自定义的可变字符串对象,并且可以使用各种字符串操作方法。如下所示:
| from collections import UserString |
| |
| |
| class user_string(UserString): |
| |
| |
| def append(self, new): |
| self.data = self.data + new |
| |
| |
| def remove(self, s): |
| self.data = self.data.replace(s, "") |
| |
| text = 'dog cat lion elephant' |
| |
| animals = user_string(text) |
| animals.append("monkey") |
| |
| for word in ['cat', 'elephant']: |
| animals.remove(word) |
| print(animals) |
6.3 UserDict
UserDic是一个字典类型的包装类,用于创建自定义字典类。通过继承UserDict类,可以创建自定义的字典对象。如下所示:
| from collections import UserDict |
| |
| class MyDict(UserDict): |
| def __init__(self, initialdata=None): |
| super().__init__(initialdata) |
| |
| def __setitem__(self, key, value): |
| |
| super().__setitem__(key.upper(), value) |
| |
| |
| my_dict = MyDict() |
| |
| |
| my_dict['name'] = 'Alice' |
| my_dict['age'] = 25 |
| |
| |
| print(my_dict) |
| |
| {'NAME': 'Alice', 'AGE': 25} |
7 参考