pin_drop当前位置:知识文库 ❯ 图文

Python列表去重如何保持顺序?90%的人不知道的dict.fromkeys()技巧

发布日期:2026-03-10 作者:小齐智能

为了去个重,我差点把列表顺序搞没了!Python“顺序去重”的血泪史

大家好啊,我是你们喜欢瞎折腾代码的那个谁。

事情是这样的,前两天我在处理一坨用户行为日志的数据。需求很简单,就是把一堆用户ID去个重,但是呢,顺序千万不能乱,因为ID的顺序代表了用户操作的时间线。我当时想都没想,抬手就是一行:

代码示例

user_ids = [102, 101, 102, 103, 101, 104, 105]
clean_ids = list(set(user_ids))
print(clean_ids)

结果输出一看,我人傻了:[101, 102, 103, 104, 105]。顺序全给我打乱了!虽然id是去重了,但这玩意儿传到下一个流程里,领导一看,时间线都对不上,不得把我叼飞?

今天就聊聊Python里这个看似简单、实则暗藏杀机的列表顺序去重问题。全是干货,而且是那种你收藏了下次遇到直接复制就能用的那种。

先甩结论:90%的情况用这一行代码就够了

如果你用的是 Python 3.7+ (现在应该没人用3.6以下的了吧?),而且列表里的元素都是简单的数字、字符串这种可哈希的类型,啥都别想,直接用这行神代码:

代码示例

user_ids = [102, 101, 102, 103, 101, 104, 105]
clean_ids = list(dict.fromkeys(user_ids))
print(clean_ids)  # 输出: [102, 101, 103, 104, 105]

看到没?102 在最前面,101 在第二位,原来啥顺序,去重后还是啥顺序

原理其实特简单,从Python 3.7开始,字典会记住你插入键的顺序 -。dict.fromkeys()会用列表的元素当作字典的键,自动把重复的过滤掉,最后再把这个有序字典的键转回列表。完美!

如果你还差点踩坑:不可哈希的“脏数据”

但是!生活总是充满惊喜(惊吓)。我昨天遇到的情况就没那么简单。我那个列表里存的不是单纯的ID,而是字典!

比如这种,每个元素是个字典

代码示例

log_data = [
    {"user_id": 102, "action": "click"},
    {"user_id": 101, "action": "view"},
    {"user_id": 102, "action": "click"},  # 这玩意儿要删掉
    {"user_id": 103, "action": "scroll"},
]

这时候如果你还想用 dict.fromkeys(),Python解释器直接报错给你看:TypeError: unhashable type: 'dict' -咋整?手动撸一个循环吧。

代码示例

def deduplicate_dicts(data_list):
    seen = set()
    result = []
    for item in data_list:

把字典转成字符串或者frozenset,这样才能放到set里比较简单点,如果字典结构简单,就转成json字符串

代码示例

item_key = json.dumps(item, sort_keys=True)  # sort_keys保证键的顺序一致
if item_key not in seen:
    seen.add(item_key)
    result.append(item)
    return result

调用

代码示例

clean_log = deduplicate_dicts(log_data)
print(clean_log)

输出: [{"user_id": 102, "action": "click"}, {"user_id": 101, "action": "view"}, {"user_id": 103, "action": "scroll"}]

你看,第二个重复的 102 click 就被干掉了,而且顺序保留了第一个出现的位置 -

再来点硬核的:海量数据怎么办?

如果你处理的不是上面那种几百条的数据,而是几百万行的日志,内存直接给你干爆。

这时候就得用生成器了。生成器就像个抠门的地主,你要一点,他给你一点,绝不提前多给。

代码示例

def unique_generator(file_path):
    seen = set()
    with open(file_path, 'r') as f:
    for line in f:
    line = line.strip()
    if line not in seen:
    seen.add(line)
    yield line  # 用yield,每次只吐一行

使用的时候

代码示例

for unique_line in unique_generator('huge_log.txt'):

在这里一条一条处理,内存稳稳的

代码示例

print(unique_line)

这种流式处理的方式,别说百万数据,就是TB级的文件也能轻松应对 -

性能党看过来

有人纠结到底哪个快?我做了一点点小测试(当然没人家专业网站那么严谨),结论是:

list(dict.fromkeys()):代码最优雅,速度极快,首选 -手写 for 循环 + set:其实更快!因为少了一层字典转换的开销,代码可读性也高 -这才是性能王者,而且简单易懂

代码示例

seen = set()
result = []
for item in huge_list:
    if item not in seen:
        seen.add(item)
        result.append(item)

列表推导式那种骚操作:虽然写起来显得很牛逼,但可读性差点,性能也没啥提升,不推荐装这个逼。

总结一下

为了去个重,真的没必要像我最早那样直接用 set() 把顺序丢了。记住这三点:

简单数据,要保序:list(dict.fromkeys(your_list)) 或者手写循环+set -

列表里有字典/列表:手动遍历,把元素转成可哈希的形式(如json字符串)再判断 -

数据量大到爆内存:用生成器,流式处理

好了,今天的坑我就帮你踩平了。下次去重的时候,别再丢顺序了,不然产品经理提刀来找你,别说我没提醒你!

有啥问题评论区见,我虽然不一定秒回,但看到了一定回。

本文涉及AI创作

内容由AI创作,请仔细甄别

list快速访问

上一篇: 秒懂PHP图片上传失败排查:Nginx的client_max_body_size配置详解 下一篇: 无

poll相关推荐