Python collections 模块深度应用

11次阅读

defaultdict应传入可调用对象而非调用结果,如defaultdict(list)正确,defaultdict(list())错误;most_common(n)返回普通list,非counter子集;namedtuple不可变,修改需用_replace();deque设maxlen会静默丢弃旧元素。

Python collections 模块深度应用

defaultdict 初始化时传入可调用对象,不是调用结果

很多人写 defaultdict(list) 没问题,但换成 defaultdict(list()) 就报错或行为异常——后者会立即执行 list(),返回空列表对象,导致所有键共享同一个 list 实例。

正确做法是传入类型名或 Lambda

  • defaultdict(list) —— 安全,每次缺省时调用 list()
  • defaultdict(lambda: []) —— 灵活,适合带参数的构造(如 lambda: [0] * 3
  • defaultdict(int) 等同于 defaultdict(lambda: 0),但更简洁

注意:传入函数必须无参,否则运行时报 TypeError: () takes 0 positional arguments but 1 was given——因为 defaultdict 内部会自动传入缺失的 key 给工厂函数(仅当工厂函数支持该签名时才生效;多数内置类型如 listint 忽略参数,但自定义函数需显式接收并忽略)。

Counter 的 most_common(n) 返回 list,不是 Counter 子集

most_common(3) 返回的是 [(key, count), ...] 形式的普通 list,不保留 Counter 类型,也不能直接继续链式调用 update()elements()

立即学习Python免费学习笔记(深入)”;

常见误用场景:

  • 想取 top-3 后再过滤:得先转回 Counter(dict(c.most_common(3)))
  • 想按频次排序但保留计数器行为:用 sorted(c.items(), key=lambda x: x[1], reverse=True)[:3] 更可控
  • c.most_common() 不传参时返回全部,性能差(O(n log n)),大数据量慎用

另外,Counter 对负数计数不做限制,但 elements() 只产出正计数项——这点常被忽略,导致遍历时“消失”了减过次数的 key。

namedtuple 是不可变对象,_replace() 才是修改入口

定义 Point = namedtuple('Point', ['x', 'y']) 后,p = Point(1, 2) 的字段不能直接赋值:p.x = 3 会触发 AttributeError

修改唯一合法方式是 _replace()

  • p._replace(x=3) 返回新实例,原 p 不变
  • 只接受关键字参数,不支持位置传参;未提供的字段保持原值
  • 字段名含下划线(如 _id)会被自动过滤,除非显式设 rename=True

注意:_replace() 不校验类型或范围,也不会触发任何钩子——它就是浅拷贝+字典更新。如果需要验证逻辑,应封装成普通 class,别硬套 namedtuple

deque 的 maxlen 参数会静默丢弃旧元素,不是警告或报错

初始化 d = deque(maxlen=3) 后,持续 append() 超过容量时,左端元素被自动踢出,不抛异常也不提示。

这在滑动窗口类场景很实用,但也容易埋坑:

  • 调试时发现数据“变少”,却没意识到是 maxlen 在起作用
  • 想检测是否发生截断?得自己维护长度对比:if len(d) == d.maxlen and len(d) > 0:
  • extend() 一次性加多个时,同样受 maxlen 约束,但行为是“整体插入后截断”,不是逐个判断

真正需要“满则报错”的场景,别用 deque,改用普通 list + 显式长度检查更清晰。

text=ZqhQzanResources