python collections模块
collections.Counter([iterable-or-mapping]
Counter
是用来对对象进行计数,主要是针对于重复元素的个数的计数,详细如下:
from collections import Counter
c = Counter('gallahad')
print(c)
c = Counter({'red': 4, 'blue': 2})
print(c)
c = Counter(cats=4, dogs=8)
print(c)
Counter({'a': 3, 'l': 2, 'g': 1, 'h': 1, 'd': 1})
Counter({'red': 4, 'blue': 2})
Counter({'dogs': 8, 'cats': 4})
由此可见Counter
返回的是以(元素:个数)为键值对组成的字典
,如果传入的是字典或者是关键字参数,则之间返回对应字典。
由上述结构可见,Counter
是dict的一个子类,用于计数可哈希对象。它是一个集合,元素像字典键(key)一样存储,它们的计数存储为值。因此我们可以单独取出对应元素的个数,之间使用字典的取值方法即可,当然删除字典元素也是如此,删除元素后再去访问对应的元素,虽然没有了对应的键,但不会报错,只会返回0,如下:
from collections import Counter
c = Counter('gallahad')
print(c)
print(c['a'])
del c['a']
print(c)
print(c['a'])
Counter({'a': 3, 'l': 2, 'g': 1, 'h': 1, 'd': 1})
3
Counter({'l': 2, 'g': 1, 'h': 1, 'd': 1})
0
python3.7后继承了字典的记住插入顺序的功能,在 Counter 对象上的许多操作也会保持顺序。
由于Counter
是dict的子类,因此可以使用字典的方法或属性,除此之外,还有如下方法:
- elements(): 返回计数后的字典的所有的key,是不重复的
from collections import Counter
c = Counter('gallahad')
for item in c:
print(item)
g
a
l
h
d
- most_common([n]):按照元素的出现频率,返回出现次数最高的元素(以字典形式)
from collections import Counter
c = Counter('gallahad')
print(c.most_common(2))
[('a', 3), ('l', 2)]
- subtract([iterable-or-mapping]) 3.2新增: 将Counter对象做减法处理,即将对应key的value相减
from collections import Counter
c = Counter({'red': 4, 'blue': 2, 'green': 5})
print(c)
c2 = Counter({'red': 0, 'blue': 4})
print(c2)
c.subtract(c2)
print(c)
Counter({'green': 5, 'red': 4, 'blue': 2})
Counter({'blue': 4, 'red': 0})
Counter({'green': 5, 'red': 4, 'blue': -2})
由结果可见,当两个Counter
对象中存在的元素不一致时,会默认显示多出的元素的数据。
- total():3.10新增,计
Counter
对象的结果的字典的所有value的总和。
from collections import Counter
c = Counter({'red': 4, 'blue': 2, 'green': 5})
print(c)
print(c.total())
Counter({'green': 5, 'red': 4, 'blue': 2})
11
- update([iterable-or-mapping]): 将;两个或者多个
Counter
对象合并,类似于dict.update
from collections import Counter
c = Counter({'red': 4, 'blue': 2, 'green': 5})
c2 = Counter({'red': 2, 'blue': 1, 'green': 4})
c.update(c2)
print(c)
Counter({'green': 9, 'red': 6, 'blue': 3})
Counter常用方法
c.total() # 统计Counter对象中的所有元素的个数
c.clear() # 情况Counter对象
list(c) # 去重、获取数据中的唯一的值(Counter中的key)
set(c) # 将Counter对象转为集合类型,只会有key
dict(c) # 将Counter对象转为字典类型
c.items() # 获取Counter对象的键值对,以元组形式(key, value)
Counter(dict(list_of_pairs)) # 将列表中元素为(key, value)转为字典
c.most_common()[:-n-1:-1] # 获取Counter对象前n个元素最小的元素
+c # 移除Counter对象中的元素个数为0的元素
Counter支持集合的交、并、减、加等,是对Counter对象中的元素操作
from collections import Counter
c = Counter({'red': 4, 'blue': 2, 'green': -5, "black": 0})
c2 = Counter({'red': 2, 'blue': 1, 'green': 4})
print(c - c2)
print(c + c2)
print(c & c2)
print(c | c2)
print(+c)
print(-c)
Counter({'red': 2, 'blue': 1})
Counter({'red': 6, 'blue': 3})
Counter({'red': 2, 'blue': 1})
Counter({'red': 4, 'green': 4, 'blue': 2})
Counter({'red': 4, 'blue': 2})
Counter({'green': 5})
-
相当于是subtract
,不过-
返回的是最小集,并且是直接返回,subtract
对调用元素进行操作,并且返回的是最大集。
+
相当于update
, 与上述-
与subtract
区别类似。
&
相当于选取Counter对象中的公共属性的最小值
|
相当于选取Counter对象中的公共属性的最大值
+
只显示Counter对象中的元素值为正数的
-
只显示Counter对象中元素值为负数的,显示时显示负数的绝对值
collections.defaultdict
collections.deque([iterable[, maxlen]])
- iterable: 基于创建的对象,如果没有指定,则队列为空
- maxlen:队列的最大长度,如果不设置则无限制
返回一个双向队列,从iterable
迭代对象中创建,基于栈和队列生成,顾名思义,可以进行双向的操作,拥有列表的大部分方法,除此之外,由于可以进行双向操作,插入和删除时可以指定操作的方向(从左向右还是从右向左)。
- 注意,当设置了
maxlen
最大长度之后,如果队列中元素已经满了,在对应侧添加元素将会弹出另一侧的元素,例如在左侧添加了一个元素,右侧的一个元素将会被弹出。 - 索引访问在两端的复杂度均为 O(1) 但在中间则会低至 O(n)。 如需快速随机访问,请改用列表。
deque
内置的方法:
上面介绍中有说,队列具有列表的大部分方法,例如:
- append(x): 将x添加至队列的右端
- clear(): 清空队列
- copy(): 浅拷贝,拷贝的是引用,修改数据会影响原数据
- count(x): 3.5新增,计算某个元素的出现次数
- extend(iterable): 扩展列表
- index(x[, start[, stop]]):返回x元素在队列中的位置,可以指定查找的索引区间
- insert(i, x):在索引为i的位置插入元素x
- pop():删除队列中最右侧的一个元素并返回
- remove(value):删除值为value的元素
- reverse(): 将队列进行降序排序
除此之外,由于是双向队列,会存在双向的操作,因此额外存在如下方法: - appendleft(x):将元素x添加至队列的最左侧
- extendleft(iterable):在队列的最左侧扩展可迭代对象
- popleft():删除队列中的最左侧的元素并返回
- rotate(n=1): 当n>0时,向右循环n步,相当于
appendleft(pop())
当n<0,向左循环n步,相当于append(popleft())
,为了更加直观的了解这个方法,下面将进行举例说明:
from collections import deque
d = deque([1, 2, 3], 4)
print(d)
d.rotate(1)
print(d)
d.rotate(2)
print(d)
执行流程如下:
由此可以看出一个特性,当队列使用rorate执行了len(deque) * n
次后,队列元素值不变。
常见使用场景
1.当需要对序列中的元素进行频繁的添加、删除操作时,建议使用双向队列
,而不是列表;当需要进行随机访问序列中的元素时,建议使用列表
。
2.实现类似于unix
的tail过滤功能:
例如存在如下test.txt
test1
test2
test3
test4
test5
test6
test7
test8
test9
from collections import deque
from pathlib import Path
def tail(file_path, n=5):
with open(file_path, mode="r", encoding="utf-8") as f:
return deque(f, n)
file = Path(__file__).parent.joinpath("test.txt")
print(tail(file))
deque(['test5\n', 'test6\n', 'test7\n', 'test8\n', 'test9'], maxlen=5)
3.可以使用rorate
实现切片和删除队列中的元素
from collections import deque
from typing import Deque
list_data = [1, 2, 3, 4, 5]
d = deque(list_data)
def slice(d: Deque, start: int, stop: int, step: int):
d.rotate(-start)
for i in range(stop):
d.pop()
d = filter(lambda n: n % step == 0, d)
return deque(d)
print(slice(d, 1, 3, 2))
list_data = [1, 2, 3, 4, 5]
d = deque(list_data)
def del_n(d: Deque, n):
"""删除索引为n的元素"""
d.rotate(-n)
d.popleft()
d.rotate(n)
return d
print(del_n(d, 3))
4.轮询调度器,可以在不同的迭代器中依次进行轮询第一个元素、第二个元素、第n个元素,直至迭代器结束。
def roundrobin(*iterables):
"roundrobin('ABC', 'D', 'EF') --> A D E B F C"
iterators = deque(map(iter, iterables))
print(list(iterators))
while iterators:
try:
while True:
yield next(iterators[0])
iterators.rotate(-1)
except StopIteration:
# 移除迭代完成的迭代器
iterators.popleft()
collections.defaultdict(default_factory=None, /[, ...])
- default_factory:默认的工厂,类似于是指定了字典的value值的类型。如果 default_factory 属性为 None,则调用本方法会抛出 KeyError 异常,附带参数 key。如果 default_factory 不为 None,则它会被(不带参数地)调用来为 key 提供一个默认值,这个值和 key 作为一对键值对被插入到字典中,并作为本方法的返回值返回。
返回一个新的类似字典的对象。defaultdict 是内置dict类的子类。它重载了一个方法并添加了一个可写的实例变量。
常用使用场景
1.将列表中存在重复元素的元组,转成字典,会自动将重复元素的value值添加至列表中
from collections import defaultdict
s = [('yellow', 1), ('blue', 2), ('yellow', 3), ('blue', 4), ('red', 1)]
d = defaultdict(list)
for k, v in s:
d[k].append(v)
print(d)
defaultdict(<class 'list'>, {'yellow': [1, 3], 'blue': [2, 4], 'red': [1]})
与使用dict.setdefault
类似
d = {}
for k, v in s:
# 获取key并设置默认值为[], 由于赋值赋的是列表,因此返回的类型也是列表
data: List = d.setdefault(k, [])
print(isinstance(data, List))
# 向列表中添加元素
data.append(v)
{'yellow': [1, 3], 'blue': [2, 4], 'red': [1]}
使用default_dict相比较setdefault
,将设置默认值省去了。
2.统计可迭代对象的元素个数,就是将default_factory变为int, 相当于是Counter统计元素个数一样
from collections import defaultdict
d = defaultdict(int)
data = "asdadwqeqdfa"
for k in data:
d[k] += 1
print(d)
defaultdict(<class 'int'>, {'a': 3, 's': 1, 'd': 3, 'w': 1, 'q': 2, 'e': 1, 'f': 1})
直接使用可迭代对象的count
也可以, 不过可能会出现重复操作,之所以值不会重复,是因为字典默认是无序,不可重复的。
data = "asdadwqeqdfa"
d = {}
for k in data:
d[k] = data.count(k)
print(d)
{'a': 3, 's': 1, 'd': 3, 'w': 1, 'q': 2, 'e': 1, 'f': 1}
3.default_factory设置set构建字典集合,它可以帮助我们去重。
from collections import defaultdict
s = [('red', 1), ('blue', 2), ('red', 3), ('blue', 4), ('red', 1), ('blue', 4)]
d = defaultdict(set)
for k, v in s:
d[k].add(v)
print(d)
也可以使用setdefault
from typing import Set
data = [('red', 1), ('blue', 2), ('red', 3), ('blue', 4), ('red', 1), ('blue', 4)]
d = {}
for k, v in data:
set_data: Set = d.setdefault(k, set())
set_data.add(v)
print(d)
'red': {1, 3}, 'blue': {2, 4}}
collections.namedtuple(typename, field_names, *, rename=False, defaults=None, module=None)
命名元组,命名元组赋予每个位置一个含义,提供可读性和自文档性。它们可以用于任何普通元组,并添加了通过名字获取值的能力,通过索引值也是可以的。相当于是使用命令元组可以创建了一个类,类似于type()
创建类,但它可以将类的属性一一对应。
- typename:元组子类的名称,就是创建的类的名称
- field_names:类中的参数,可以是序列,例如
['x', 'y']
, 也可以是纯字符串,'x y' 或者 'x, y'
- rename: 对于无效关键字的重命名,默认为
False
, 当为True
时,则会对关键字进行重命名,以下划线+当前位置的索引表示,例如_3
, 需要注意的是字符串的关键字是针对于field_names
中的内容,不能使用python的关键字
,对于无效关键字的认定还有重复的属性名称,。 - defaults:默认对
field_names
中属性的批量赋值,可以为None或者可迭代对象,注意defaults
中的属性是与靠右侧属性对齐。 命名元组实例没有字典,所以它们要更轻量,并且占用更小内存。
基本示例
1.下面创建了一个Person子类,并且设置其中的属性及默认值,命名元组尤其有用于赋值 csv sqlite3
模块返回的元组.
from collections import namedtuple
Person = namedtuple("Person", ["name", "age", "city"], rename=True, defaults=["南京"])
p1 = Person(name="tom", age=19, city="北京")
print(p1)
p2 = Person(name="mike", age=20)
print(p2)
Person(name='tom', age=19, city='北京')
Person(name='mike', age=20, city='南京')
常用方法
- namedtuple._make(iterable):
基于iterable
创建一个新的实例,相当于实例化了一个对象.
需要注意的是,当使用_make
时默认值失效,必须要传递默认值。
from collections import namedtuple
Person = namedtuple("Person", ["name", "age", "city"], rename=True, defaults=["南京"])
data = ["tom", 10, "北京"]
p = Person._make(data)
print(p)
Person(name='tom', age=10, city='北京')
namedtuple._asdict():返回实例对象的字典形式
from collections import namedtuple
Person = namedtuple("Person", ["name", "age", "city"], rename=True, defaults=["南京"])
p1 = Person(name="tom", age=19, city="北京")
print(p1)
print(p1._asdict())
Person(name='tom', age=19, city='北京')
{'name': 'tom', 'age': 19, 'city': '北京'}
- namedtuple._replace(**kwargs): 替换实例对象中的部分属性并返回新的实例对象
from collections import namedtuple
Person = namedtuple("Person", ["name", "age", "city"], rename=True, defaults=["南京"])
p1 = Person(name="tom", age=19, city="北京")
print(p1)
print(p1._replace(city="南京"))
print(p1)
Person(name='tom', age=19, city='北京')
Person(name='tom', age=19, city='南京')
Person(name='tom', age=19, city='北京')
- namedtuple._fields: 返回当前实例对象的所有属性名,以元组形式返回
from collections import namedtuple
Person = namedtuple("Person", ["name", "age", "city"], rename=True, defaults=["南京"])
p1 = Person(name="tom", age=19, city="北京")
print(p1)
print(p1._fields)
Person(name='tom', age=19, city='北京')
('name', 'age', 'city')
- namedtuple._field_defaults: 返回命名元组的设置的默认属性及默认值,以字典形式返回
from collections import namedtuple
Person = namedtuple("Person", ["name", "age", "city"], rename=True, defaults=["南京"])
p1 = Person(name="tom", age=19, city="北京")
print(p1)
print(p1._field_defaults)
Person(name='tom', age=19, city='北京')
{'city': '南京'}
常用实践
1.获取实例对象的属性的值,可以使用对象.属性
,也可以使用getattr(对象,属性)
。
from collections import namedtuple
Person = namedtuple("Person", ["name", "age", "city"], rename=True, defaults=["南京"])
p1 = Person(name="tom", age=19, city="北京")
print(p1)
print(p1.name)
print(getattr(p1, "name"))
2.转换一个字典到命名元组,使用 ** 两星操作符
from collections import namedtuple
Person = namedtuple("Person", ["name", "age", "city"], rename=True, defaults=["南京"])
dict_data = {'name': 'tom', 'age': 19, 'city': '北京'}
print(Person(**dict_data))
Person(name='tom', age=19, city='北京')
3.可以通过继承命名元素,实现类,移除__dict__
从而降低内存开销
from collections import namedtuple
Person = namedtuple("Person", ["name", "age", "city"], rename=True, defaults=["南京"])
class Person(Person):
__slot__ = []
def run(self):
print(self.name, self.age, self.city)
p = Person(name='TOM', age=12)
print(p)
Person(name='TOM', age=12, city='南京')
collections.ChainMap(*maps)
将多个字典或者其他映射组合在一起,创建一个单独的可更新的视图,说通俗点就是将多个字典合起来,
对于重复的key值默认会取第一个字典中的值
from collections import ChainMap
from typing import Iterable
a = {"x": 1, "y": 2}
b = {"x": 2, "z": 3}
c = {"y": 3, "z": 4}
d = ChainMap(a, b, c)
print(d)
print(isinstance(d, Iterable))
print(d.get("x", None))
print(d.get("y", None))
print(d.get("z", None))
ChainMap({'x': 1, 'y': 2}, {'x': 2, 'z': 3}, {'y': 3, 'z': 4})
True
1
2
3
由结果可以看出,虽然结合在了一起,但是内部还是存储的字典,并且返回的是一个可迭代对象,重复元素取的都是第一个获取到的元素
对于新增或者修改元素时,只会更新第一个字典 ;对于删除时,默认只会删除第一个字典中的数据,如果第一个字典中没有需要删除的元素,则会直接报错
from collections import ChainMap
from typing import Iterable
a = {"x": 1, "y": 2}
b = {"x": 2, "z": 3}
c = {"y": 3, "z": 4}
d = ChainMap(a, b, c)
print(d)
d['x'] = 2
print(d)
d['z'] = 3
print(d)
del d['x']
del d['w']
ChainMap({'x': 1, 'y': 2}, {'x': 2, 'z': 3}, {'y': 3, 'z': 4})
ChainMap({'x': 2, 'y': 2}, {'x': 2, 'z': 3}, {'y': 3, 'z': 4})
ChainMap({'x': 2, 'y': 2, 'z': 3}, {'x': 2, 'z': 3}, {'y': 3, 'z': 4})
Traceback (most recent call last):
File "D:\programs\python3.10\lib\collections\__init__.py", line 1042, in __delitem__
del self.maps[0][key]
KeyError: 'w'
KeyError: "Key not found in the first mapping: 'w'"
由结果也可以看出,对于新增、更新、删除只会操作第一个字典。
对字典操作的影响会直接影响到第一个字典
还是沿用上述的字典
from collections import ChainMap
from typing import Iterable
a = {"x": 1, "y": 2}
b = {"x": 2, "z": 3}
c = {"y": 3, "z": 4}
d = ChainMap(a, b, c)
print(d)
d['x'] = 2
print(d)
d['z'] = 3
print(d)
print(a)
hainMap({'x': 1, 'y': 2}, {'x': 2, 'z': 3}, {'y': 3, 'z': 4})
ChainMap({'x': 2, 'y': 2}, {'x': 2, 'z': 3}, {'y': 3, 'z': 4})
ChainMap({'x': 2, 'y': 2, 'z': 3}, {'x': 2, 'z': 3}, {'y': 3, 'z': 4})
{'x': 2, 'y': 2, 'z': 3}
由结果可以看出,对于d的操作影响到了它的第一个字典c,相当于是浅拷贝
说了这么多,其实对于字典的合并还有一个自带的update,不过它会另外破坏字典的原始结构,并且创建后的字典于创建之前的字典没有任何关系,相当于深拷贝。
collections.OrderedDict()
通过名称,我们可以得知,这个其实也是一个字典,不过它可以记住插入时的顺序,从而在输出时按照插入时的顺序进行输出,但是此对象会占用较大内存,为一个普通字典的两倍。主要用于修改字典中的顺序。
from collections import OrderedDict
dict_data = OrderedDict()
dict_data["key1"] = 1
dict_data["key2"] = 2
dict_data["key3"] = 3
dict_data["key4"] = 4
for key, val in dict_data.items():
print(key, val)
data = OrderedDict.fromkeys("abcvn")
data.move_to_end("b", last=True)
print(data)
data.move_to_end("n", last=False)
print(data)
key1 1
key2 2
key3 3
key4 4
OrderedDict([('a', None), ('c', None), ('v', None), ('n', None), ('b', None)])
OrderedDict([('n', None), ('a', None), ('c', None), ('v', None), ('b', None)])
注意事项
-
OrderedDict 内部维护着一个根据键插入顺序排序的双向链表。每次当一个新的元素插入进来的时候, 它会被放到链表的尾部。对于一个已经存在的键的重复赋值不会改变键的顺序。
-
一个 OrderedDict 的大小是一个普通字典的两倍,因为它内部维护着另外一个链表。 所以如果你要构建一个需要大量 OrderedDict 实例的数据结构的时候(比如读取 100,000 行 CSV 数据到一个 OrderedDict 列表中去), 那么你就得仔细权衡一下是否使用 OrderedDict 带来的好处要大过额外内存消耗的影响。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 25岁的心里话
· 按钮权限的设计及实现