答案:Python中去重常用set、dict.fromkeys()和循环加辅助集合;set最快但无序,dict.fromkeys()可保序且高效,循环法灵活支持复杂对象去重。

删除列表中的重复元素,在Python中我们通常会利用集合(set)的特性,或者通过列表推导式、循环遍历等方式实现。每种方法都有其适用场景和性能考量,关键在于理解数据本身的特点以及对最终结果(如是否需要保留原始顺序)的要求。
在Python中,处理列表去重有几种核心方法,它们各有优劣,适用于不同的场景。
1. 利用集合(Set)的特性 这是最常见也通常是最简洁高效的方法,尤其适用于元素可哈希且不关心原始顺序的场景。
my_list = [1, 2, 2, 3, 4, 4, 5, 1, 6] unique_list = list(set(my_list)) print(unique_list) # 输出: [1, 2, 3, 4, 5, 6] (顺序可能不同)
原理:
set
TypeError
2. 使用 dict.fromkeys()
my_list = [1, 2, 2, 3, 4, 4, 5, 1, 6] unique_list = list(dict.fromkeys(my_list)) print(unique_list) # 输出: [1, 2, 3, 4, 5, 6] (保留原始顺序)
原理:
dict.fromkeys(iterable)
iterable
None
3. 循环遍历配合辅助集合(保留顺序且处理不可哈希元素前的准备) 当你需要保留原始顺序,并且可能需要更精细的控制,或者列表元素可能不可哈希时,这种方法提供了一种更通用的思路。
my_list = [1, 2, 2, 3, 4, 4, 5, 1, 6]
unique_list = []
seen = set() # 用一个set来跟踪已经见过的元素
for item in my_list:
if item not in seen:
unique_list.append(item)
seen.add(item)
print(unique_list)
# 输出: [1, 2, 3, 4, 5, 6] (保留原始顺序)原理: 遍历原列表,用一个
set
seen
seen
seen
set
dict.fromkeys()
选择哪种去重策略,往往不是“最好”与“最差”的问题,而是“最适合”与“不适合”的考量。在我看来,这背后是对效率、代码可读性以及特定场景需求的综合权衡。
首先,Set转换法无疑是最直观、最“Pythonic”的去重方式,尤其当你的列表元素都是像数字、字符串、元组这类可哈希类型,并且你对元素的最终顺序没有严格要求时。它的底层实现通常是哈希表,查找和插入的平均时间复杂度接近O(1),因此在大数据量下去重效率极高。但它的局限性也很明显,一旦遇到列表、字典这类不可哈希的对象,它就会直接报错,这在处理复杂数据结构时是个障碍。
其次,dict.fromkeys()
set
最后,循环遍历配合辅助集合的方法,虽然代码量相对多一些,但它的通用性和灵活性是前两者无法比拟的。当你的列表元素不可哈希,或者你需要根据元素的某个特定属性(而不是整个元素)来判断重复时,这种方法就能派上用场。例如,你有一堆自定义对象,你可能需要根据它们的
id
seen
in
in
总的来说,如果数据简单且顺序不重要,用
set
dict.fromkeys()
当列表中的元素不再是简单的数字或字符串,而是嵌套列表、字典、自定义对象时,去重就变得有些棘手了。因为
set
dict.fromkeys()
1. 处理嵌套列表或字典(不可哈希对象)
直接将包含列表或字典的列表转换为
set
TypeError: unhashable type: 'list'
unhashable type: 'dict'
将不可哈希元素转换为可哈希形式: 如果嵌套列表的内部元素是可哈希的,可以将其转换为元组(tuple),因为元组是不可变的,因此是可哈希的。字典则可以转换为
frozenset
list_of_lists = [[1, 2], [3, 4], [1, 2], [5, 6], [3, 4]]
# 将内部列表转换为元组,然后使用set去重
unique_lists_tuples = set(tuple(item) for item in list_of_lists)
unique_lists = [list(item) for item in unique_lists_tuples]
print(unique_lists)
# 输出: [[1, 2], [3, 4], [5, 6]] (顺序不确定)
list_of_dicts = [{'a': 1, 'b': 2}, {'b': 2, 'a': 1}, {'c': 3}]
# 字典转换为frozenset,前提是字典的键和值都是可哈希的
# 注意:frozenset不保证顺序,且键值对需要转换为元组
unique_dicts_frozenset = set(frozenset(d.items()) for d in list_of_dicts)
unique_dicts = [dict(item) for item in unique_dicts_frozenset]
print(unique_dicts)
# 输出: [{'a': 1, 'b': 2}, {'c': 3}] (顺序不确定,且frozenset可能打乱原始字典键值对的顺序)这种方法虽然有效,但需要注意转换过程可能带来的数据结构变化和顺序问题。
自定义比较逻辑配合辅助集合: 这是最灵活的方式,尤其适用于当“重复”的定义比较复杂时。你可以定义一个函数来为每个复杂对象生成一个“哈希键”,然后用这个键来判断重复。
# 假设我们有一堆用户字典,我们认为只要'id'相同就是重复用户
users = [
{'id': 1, 'name': 'Alice', 'age': 30},
{'id': 2, 'name': 'Bob', 'age': 25},
{'id': 1, 'name': 'Alicia', 'age': 31}, # id为1的重复
{'id': 3, 'name': 'Charlie', 'age': 35},
{'id': 2, 'name': 'Robert', 'age': 26} # id为2的重复
]
unique_users = []
seen_ids = set() # 用来存储已经见过的用户ID
for user in users:
user_id = user['id'] # 提取作为判断重复的“键”
if user_id not in seen_ids:
unique_users.append(user)
seen_ids.add(user_id)
print(unique_users)
# 输出: [{'id': 1, 'name': 'Alice', 'age': 30}, {'id': 2, 'name': 'Bob', 'age': 25}, {'id': 3, 'name': 'Charlie', 'age': 35}]这种方法保留了原始对象的完整性,并且可以根据业务逻辑精确定义“重复”的含义。
2. 处理自定义对象
如果你的列表包含自定义类的实例,并且你希望根据它们的某个或某些属性来去重,你有两种主要做法:
重写 __eq__
__hash__
set
class Product:
def __init__(self, sku, name, price):
self.sku = sku
self.name = name
self.price = price
def __eq__(self, other):
if not isinstance(other, Product):
return NotImplemented
return self.sku == other.sku # 假设sku是唯一标识
def __hash__(self):
return hash(self.sku) # 必须与__eq__逻辑一致
def __repr__(self):
return f"Product(sku='{self.sku}', name='{self.name}', price={self.price})"
products = [
Product('A001', 'Laptop', 1200),
Product('A002', 'Mouse', 25),
Product('A001', 'Gaming Laptop', 1500), # sku重复
Product('A003', 'Keyboard', 75),
Product('A002', 'Wireless Mouse', 30) # sku重复
]
unique_products = list(set(products))
print(unique_products)
# 输出: [Product(sku='A001', name='Laptop', price=1200), Product(sku='A002', name='Mouse', price=25), Product(sku='A003', name='Keyboard', price=75)]注意: 重写
__eq__
__hash__
a == b
hash(a) == hash(b)
使用辅助集合提取关键属性去重(不修改类) 如果不想修改类的定义,或者去重逻辑只是临时性的,可以使用上面处理字典的类似方法:提取对象的某个(或多个)属性作为哈希键。
# 沿用上面的Product类,但假设我们不能修改它
products_no_hash = [
Product('A001', 'Laptop', 1200),
Product('A002', 'Mouse', 25),
Product('A001', 'Gaming Laptop', 1500),
Product('A003', 'Keyboard', 75),
Product('A002', 'Wireless Mouse', 30)
]
unique_products_by_sku = []
seen_skus = set()
for product in products_no_hash:
if product.sku not in seen_skus:
unique_products_by_sku.append(product)
seen_skus.add(product.sku)
print(unique_products_by_sku)
# 输出: [Product(sku='A001', name='Laptop', price=1200), Product(sku='A002', name='Mouse', price=25), Product(sku='A003', name='Keyboard', price=75)]这种方法灵活,不需要修改原始类,但需要手动编写循环逻辑。
在实际开发中,面对去重需求,我通常会从以下几个维度来思考和选择最合适的方案:
1. 数据规模和性能要求:
list(set(my_list))
list(dict.fromkeys(my_list))
set
dict.fromkeys()
item not in new_list
set
in
2. 是否需要保留原始顺序:
list(set(my_list))
list(dict.fromkeys(my_list))
set
3. 列表元素的类型:
set
set
以上就是如何删除列表中的重复元素?的详细内容,更多请关注php中文网其它相关文章!
每个人都需要一台速度更快、更稳定的 PC。随着时间的推移,垃圾文件、旧注册表数据和不必要的后台进程会占用资源并降低性能。幸运的是,许多工具可以让 Windows 保持平稳运行。
Copyright 2014-2025 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号