Python 保序去重:dict.fromkeys 的适用边界与测试方法

举报
yd_254857454 发表于 2026/11/07 16:09:11 2026/11/07
【摘要】 用一个已运行的最小示例解释 Python dict.fromkeys 如何保留首次出现顺序,并讨论可哈希限制、类型相等、业务键与测试边界。

先明确“重复”的含义

处理一组标签、编号或命令行参数时,经常希望删除重复项,同时保留每个值第一次出现的位置。这里的顺序不是排序结果,而是输入顺序。例如输入 [3, 1, 3, 2, 1],期望输出 [3, 1, 2]。本文讨论内存中的教学数据,不涉及线上业务或性能实测。

一个短小实现

def unique_in_order(items):
    return list(dict.fromkeys(items))
values = [3, 1, 3, 2, 1]
result = unique_in_order(values)
assert result == [3, 1, 2]
print(result)

dict.fromkeys 把输入元素用作字典键,值默认为 None。相等的键不会产生新的位置,首次插入顺序被保留。Python 3.7 起,字典保持插入顺序是语言保证;再对字典调用 list,得到的就是键列表。
这个最小示例在本次 DevSpace 免费体验环境中已保存为 Python 文件并运行,输出 [3, 1, 2],断言通过。它验证的是这个输入,不代表所有输入类型都适用。

边界一:元素必须可哈希

整数、字符串通常可以用作键;列表和字典不能直接用作键。输入 [[1, 2], [1, 2]] 时,上面的函数会抛出 TypeError。元组也不是天然都可哈希,例如包含列表的元组仍然不可哈希。
如果输入是记录,应先明确业务键。例如按编号去重,并保留第一条记录:

def first_by_id(records):
    seen = set()
    result = []
    for record in records:
        key = record["id"]
        if key not in seen:
            seen.add(key)
            result.append(record)
    return result

此处要求每条记录存在 id,且 id 可哈希。缺失字段、空编号和大小写是否等价,都应在调用前明确,而不是悄悄吞掉异常。result 中保存的是原记录引用,不是深拷贝。

边界二:类型不同也可能被视为同一个键

字典去重依据哈希和相等关系,而非显示形式或类型名称。例如 1 == 1.0 == True 为真,它们作为字典键时会合并。如果业务要求整数 1 与布尔值 True 不同,可考虑使用 (type(value), value) 作为去重键,但 value 本身仍需可哈希。这个策略也必须符合业务定义,不能只为了消除报错而随意转换成字符串。

边界三:去重与计数是不同需求

保序去重只保留“是否出现过”,不会保留出现次数;需要频次时应选择 Counter 等计数方式。list(set(items)) 虽然能去重,却不能作为保留首次出现顺序的接口承诺。sorted(set(items)) 又引入了排序,并要求值之间可比较。三个目标不应混用。

用断言把约定写清楚

下面是可自行运行的补充测试,不把它们当作上文已执行的测试结果:

assert unique_in_order([]) == []
assert unique_in_order(["b", "a", "b"]) == ["b", "a"]
assert len(unique_in_order([1, 1.0, True])) == 1
try:
    unique_in_order([[1], [1]])
except TypeError:
    pass
else:
    raise AssertionError("列表元素应触发不可哈希错误")

一般情况下,哈希查找使整体时间近似线性,额外空间随不同元素的数量增加;这不是对恶意哈希碰撞或昂贵自定义相等比较的最坏情况保证。超大输入还要考虑是否真的需要一次性创建完整结果列表。
结论很简单:先定义相等规则、保留首次还是末次、是否需要次数,再选择容器。短代码值得用,但它的输入契约应当比代码更清楚。

【声明】本内容来自华为云开发者社区博主,不代表华为云及华为云开发者社区的观点和立场。转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息,否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0)

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。