Python中如何从列表中移除可变对象的重复项?
去除列表中可变对象的重复项
不可变对象的常规去重
当列表由不可变对象(比如整数、字符串)组成时,去除重复项直接用set(lst)即可:
lst = [0,4,2,6,3,6,4,9,2,2] # Python中整数是不可变类型 print(set(lst)) # {0,2,3,4,6,9}
可变对象去重的痛点
如果列表包含可变对象(比如dict、list),set(lst)会直接报错——因为可变对象不可哈希,会触发TypeError: unhashable type: '<type>'。
举个例子,下面的列表里有多个重复引用的dict:
d0 = {0:'a', 1:'b', 9:'j'} d1 = {'jan':1, 'jul':7, 'dec':12} d2 = {'hello':'hola', 'goodbye':'adios', 'happy':'feliz', 'sad':'triste'} lst = [d0, d1, d1, d0, d2, d1, d0]
我们希望遍历列表时每个dict只处理一次,但用set(lst)会抛出TypeError: unhashable type: 'dict'。
基础实现:利用对象ID去重
可以通过对象的id()来判断是否为同一对象(同一对象的ID唯一),实现一个生成器函数:
def dedup(lst): seen_ids = set() for elem in lst: id_ = id(elem) if id_ not in seen_ids: seen_ids.add(id_) yield elem
更优的实现方案
1. 用itertools简化逻辑
借助itertools.filterfalse封装判断逻辑,代码更简洁模块化:
from itertools import filterfalse def dedup(lst): seen_ids = set() def is_duplicate(elem): elem_id = id(elem) if elem_id in seen_ids: return True seen_ids.add(elem_id) return False return filterfalse(is_duplicate, lst)
功能和基础实现完全一致,但结构更清晰。
2. 按对象内容去重(而非引用)
如果你的需求是内容相同的可变对象也视为重复(比如两个不同但键值对完全一样的dict),上面的方法就不适用了。这时可以把可变对象转换成可哈希的类型,比如将dict转成排序后的元组:
def dedup_by_value(lst): seen = set() for elem in lst: # 对dict的items排序后转元组,确保内容相同的dict生成相同的键 key = tuple(sorted(elem.items())) if key not in seen: seen.add(key) yield elem
比如对于d0 = {0:'a',1:'b'}和d3 = {1:'b',0:'a'},这个方法会将它们视为重复项,只保留第一个出现的对象。
3. 直接返回列表(非生成器)
如果不需要生成器,而是要直接得到去重后的列表,可以用常规的循环+列表的写法:
def dedup_list(lst): seen_ids = set() result = [] for elem in lst: elem_id = id(elem) if elem_id not in seen_ids: seen_ids.add(elem_id) result.append(elem) return result
这个写法更直观,适合不需要惰性求值的场景。
内容的提问来源于stack exchange,提问作者joseville
相关产品推荐
相关产品推荐

