Python collections 模块的高阶用法

7次阅读

defaultdict通过指定默认工厂函数(如list、int)按需生成缺失键的值,避免KeyError;错误写法是传不可调用对象如[],正确写法为defaultdict(list)。

Python collections 模块的高阶用法

defaultdict 怎么避免键不存在时抛 KeyError

普通字典访问不存在的键会触发 KeyError,而 defaultdict 在初始化时指定默认工厂函数,能自动处理缺失键。关键不是“设个默认值”,而是“按需生成”——比如用 list 作工厂,每次遇到新键就新建一个空列表,而不是复用同一个对象。

  • 错误写法:defaultdict([]) —— 这会报错,因为 [] 不是可调用对象
  • 正确写法:defaultdict(list)(注意没括号)、defaultdict(int)defaultdict(Lambda: "N/A")
  • 慎用可变默认值:若用 defaultdict(lambda: []) 没问题,但若误写成 defaultdict(lambda x=[]: x),会导致所有键共享同一列表

Counter 统计字符串/列表时怎么保留原始顺序

Counter 本身继承dict,在 python 3.7+ 中保持插入顺序,但它的 most_common() 方法返回的是按频次排序的结果,不反映首次出现顺序。如果需要“先出现的优先,频次相同时按原序排”,得手动处理。

  • 直接用 Counter(items).keys() 可得去重后按首次出现顺序排列的元素(Python 3.7+)
  • 要实现“频次降序 + 首次出现升序”双条件排序:sorted(Counter(items).items(), key=lambda x: (-x[1], items.index(x[0]))),但注意 items.index 效率低,大数据量建议预建索引映射
  • Counter 支持减法(c1 - c2)和取正(+c),但负频次会被自动过滤掉,这点容易被忽略

namedtuple 定义后怎么安全地添加新字段

namedtuple 实例是不可变的,定义后无法动态加字段。所谓“添加字段”只能是创建新类型或转换为可变结构。强行用 _replace() 只能更新已有字段,不能新增。

  • 若只是临时需要扩展,转成 dict 最直接:my_tuple._asdict(),然后增删键
  • 若需长期支持字段扩展,改用 types.SimpleNamespacedataclass(Python 3.7+),它们天生支持属性赋值
  • namedtuple._fields + ("new_field",) 拼接字段名再调用 namedtuple 重建类型,可行但破坏封装,且旧实例无法直接升级

deque 做队列时为什么 pop() 比 pop(0) 快得多

deque 是双向链表实现,pop()popleft() 都是 O(1),但 pop(0) 是 list 的方法,而 list 是连续数组,删除首元素需整体前移,复杂度 O(n)。混用 deque 和 list 的操作习惯是常见性能陷阱。

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

  • 别对 dequed.pop(0) —— 虽然语法合法,但退化为慢路径,应改用 d.popleft()
  • deque(maxlen=N) 启用最大长度后,append 会自动丢弃另一端元素,适合滑动窗口场景;但此时 rotate() 行为受 maxlen 影响,容易出意料
  • deque 不支持切片(如 d[1:3]),也不支持 in 查找(O(n)),高频查找请换回 list 或 set

实际用 collections 高阶功能时,最容易卡住的不是语法,而是对底层数据结构行为的误判——比如把 deque 当 list 用、把 namedtuple 当可变容器用、或者以为 Counter 的构造过程会保留输入顺序(其实只保证结果字典顺序,不等于输入顺序)。

text=ZqhQzanResources