python怎么从列表中删除重复项_python列表去重高效实现方法

冰火之心
发布: 2025-09-15 23:39:01
原创
779人浏览过
Python列表去重的核心思路是利用集合的唯一性或遍历记录元素。最高效方法是使用set,但不保留顺序;若需保留顺序,推荐collections.OrderedDict.fromkeys()或列表推导式结合辅助set,两者均高效且保持O(n)时间复杂度;对于不可哈希对象,可通过转换为元组或自定义__hash__和__eq__方法处理。

python怎么从列表中删除重复项_python列表去重高效实现方法

Python列表中删除重复项的核心思路,无非就是利用数据结构的特性(比如集合的唯一性),或者通过遍历并记录已出现过的元素来实现。最直接且高效的方法通常是借助Python内置的

set
登录后复制
类型,它天生就保证了元素的唯一性。如果需要保留原有顺序,则需要一些额外的技巧,比如结合
set
登录后复制
和列表遍历,或者利用
collections.OrderedDict
登录后复制

解决方案

说实话,每次遇到列表去重的问题,我脑子里首先跳出来的就是

set
登录后复制
。它简直是为去重而生。

方法一:利用

set
登录后复制
的特性(最简洁高效,但不保留顺序)

这是最Pythonic,也是我个人最喜欢的一种方法,尤其是在对元素顺序没有要求的时候。

set
登录后复制
是一个无序不重复的元素集,所以你把列表转换成
set
登录后复制
,它自然就把重复的元素给“过滤”掉了。

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

original_list = [1, 2, 2, 3, 4, 4, 5, 1]
unique_elements = list(set(original_list))
print(unique_elements) # 输出可能是 [1, 2, 3, 4, 5] 或其他顺序
登录后复制

这个方法的优点是代码极其简洁,执行效率也相当高,尤其对于大型列表。但它有个明显的“副作用”:原始列表的顺序会丢失,因为

set
登录后复制
本身就是无序的。如果你对顺序有要求,那这个方法就不太合适了。

方法二:使用循环和辅助列表(保留顺序,但效率相对低)

这种方法更像是我们用“人脑”去重的方式:遍历一遍列表,看到一个元素,如果它之前没出现过,就把它加到新列表里。

original_list = [1, 2, 2, 3, 4, 4, 5, 1]
unique_elements = []
for item in original_list:
    if item not in unique_elements:
        unique_elements.append(item)
print(unique_elements) # 输出: [1, 2, 3, 4, 5]
登录后复制

这种方法能完美保留元素的原始顺序。但效率上,当

original_list
登录后复制
非常大时,
item not in unique_elements
登录后复制
这个操作的开销会变得很大,因为它需要遍历
unique_elements
登录后复制
来查找,最坏情况下时间复杂度接近O(n^2)。所以,如果列表特别长,我一般会避免这种直接的循环查找。

方法三:利用

collections.OrderedDict.fromkeys()
登录后复制
(保留顺序,且高效)

这是一个非常优雅且高效的解决方案,它结合了字典键的唯一性和

OrderedDict
登录后复制
的顺序保持特性。
OrderedDict.fromkeys(iterable)
登录后复制
会创建一个字典,其中
iterable
登录后复制
中的元素作为键,值都为
None
登录后复制
。由于字典的键必须是唯一的,重复的元素自然就被忽略了,同时
OrderedDict
登录后复制
会记住键的插入顺序。

from collections import OrderedDict

original_list = [1, 2, 2, 3, 4, 4, 5, 1]
unique_elements = list(OrderedDict.fromkeys(original_list))
print(unique_elements) # 输出: [1, 2, 3, 4, 5]
登录后复制

我个人觉得这个方法非常巧妙,它在保证了顺序的同时,也保持了接近

set
登录后复制
的效率(平均O(n))。这是我处理需要保留顺序的去重任务时,经常会用的一个“小窍门”。

方法四:使用列表推导式与辅助

set
登录后复制
(保留顺序,高效且Pythonic)

这其实是方法二的优化版,用一个

set
登录后复制
来快速判断元素是否已出现,而不是遍历
unique_elements
登录后复制
列表。

original_list = [1, 2, 2, 3, 4, 4, 5, 1]
seen = set()
unique_elements = [item for item in original_list if item not in seen and not seen.add(item)]
print(unique_elements) # 输出: [1, 2, 3, 4, 5]
登录后复制

这里

not seen.add(item)
登录后复制
是一个常见的Python技巧。
set.add()
登录后复制
方法总是返回
None
登录后复制
,而
not None
登录后复制
True
登录后复制
。所以这个条件判断的逻辑是:如果
item
登录后复制
不在
seen
登录后复制
中,那么
item not in seen
登录后复制
True
登录后复制
seen.add(item)
登录后复制
会被执行(将
item
登录后复制
加入
seen
登录后复制
),然后
not seen.add(item)
登录后复制
也为
True
登录后复制
item
登录后复制
就会被加入
unique_elements
登录后复制
。如果
item
登录后复制
已经在
seen
登录后复制
中,那么
item not in seen
登录后复制
False
登录后复制
,整个条件判断就短路了,
item
登录后复制
不会被加入。这种写法非常Pythonic,兼顾了效率和简洁性。

Python列表去重,哪种方法最快?

要说“最快”,这其实得看具体情况和你的需求。但我们通常可以根据元素的数量级和是否需要保持顺序来做个大致的判断。

从理论上讲,基于哈希表(

set
登录后复制
dict
登录后复制
)的去重方法,平均时间复杂度是O(n),这意味着处理时间与列表长度成线性关系。而那些需要遍历列表并在另一个列表中查找元素的方法,最坏情况下可能达到O(n^2)。

  1. set()
    登录后复制
    转换法: 这是最快的,毫无疑问。因为它直接利用了Python底层对哈希表的优化。如果你对元素的原始顺序不关心,或者说,去重后重新排序对你来说不是问题,那么
    list(set(your_list))
    登录后复制
    绝对是首选。它的速度优势在大列表面前尤其明显。

  2. collections.OrderedDict.fromkeys()
    登录后复制
    : 这个方法在保持原有顺序的前提下,效率也非常高,接近
    set
    登录后复制
    转换法。它内部也是基于哈希表实现的,所以平均时间复杂度也是O(n)。对于需要保留顺序的场景,它是我个人认为性能和简洁性兼顾的最佳选择。

  3. 列表推导式与辅助

    set
    登录后复制
    : 这种方法同样保持了O(n)的平均时间复杂度,因为它用
    set
    登录后复制
    来快速判断元素是否已存在。它的性能和
    OrderedDict.fromkeys()
    登录后复制
    法非常接近,在某些微基准测试中可能会略有差异,但实际应用中基本可以认为是同级别的。

  4. 循环遍历与

    in
    登录后复制
    操作法: 这是最慢的,尤其是当
    unique_elements
    登录后复制
    列表变得很长时。每次
    item not in unique_elements
    登录后复制
    都需要线性扫描
    unique_elements
    登录后复制
    ,导致总时间复杂度上升到O(n^2)。对于小列表(比如几十个元素),你可能感觉不到差异,但对于成千上万甚至更多的元素,它会显著拖慢你的程序。

总结一下我的看法:

降重鸟
降重鸟

要想效果好,就用降重鸟。AI改写智能降低AIGC率和重复率。

降重鸟 113
查看详情 降重鸟
  • 不关心顺序,只求最快
    list(set(your_list))
    登录后复制
    ,简单粗暴有效。
  • 关心顺序,同时追求效率
    list(OrderedDict.fromkeys(your_list))
    登录后复制
    或 列表推导式加辅助
    set
    登录后复制
    ,这两者都很棒。
  • 列表非常小,且代码可读性优先:循环遍历加
    in
    登录后复制
    操作也未尝不可,但要心里有数它的性能瓶颈。

我通常会根据实际项目需求和列表规模来选择。如果不是性能瓶颈,我更倾向于代码的清晰和简洁。

处理包含不可哈希对象的Python列表去重,有什么特别技巧吗?

这确实是个让人头疼的问题!当你的列表里装着一些“不听话”的家伙,比如其他列表、字典,或者自定义的、没有实现

__hash__
登录后复制
方法的对象时,
set()
登录后复制
OrderedDict.fromkeys()
登录后复制
这些依赖哈希值的“神器”就统统失效了。Python会直接给你抛出一个
TypeError: unhashable type: 'list'
登录后复制
之类的错误。

面对这种场景,我们得换个思路,或者说,得“曲线救国”。

技巧一:手动遍历,并自定义“相等”判断

这是最通用但也最“笨拙”的方法,但它能处理一切情况。你需要自己定义什么是“重复”。

original_list_of_lists = [[1, 2], [3, 4], [1, 2], [5, 6], [3, 4, 5]]
unique_elements = []

for item in original_list_of_lists:
    # 这里的关键是判断 item 是否已存在于 unique_elements 中
    # 对于列表,Python默认的 == 操作符会进行值比较
    if item not in unique_elements:
        unique_elements.append(item)

print(unique_elements) # 输出: [[1, 2], [3, 4], [5, 6], [3, 4, 5]]
登录后复制

这种方法的核心在于

item not in unique_elements
登录后复制
这一步。对于列表、字典这类不可哈希对象,Python会使用它们的
__eq__
登录后复制
方法进行值比较。它的缺点是效率低下,和之前提到的O(n^2)方法一样,不适合处理大型列表。

技巧二:将不可哈希对象转换为可哈希的“代理”形式

这是我个人觉得比较优雅且高效的解决方案,前提是你的不可哈希对象能被可靠地转换为可哈希的形式。

  • 对于列表的列表(list of lists): 我们可以把内部的列表转换为元组(tuple),因为元组是不可变的,因此是可哈希的。

    original_list_of_lists = [[1, 2], [3, 4], [1, 2], [5, 6], [3, 4, 5]]
    
    # 将内部列表转换为元组,然后用set去重
    # 这里用map很简洁,也可以用列表推导式
    tuple_list = list(map(tuple, original_list_of_lists))
    unique_tuples = list(set(tuple_list))
    
    # 如果需要,再转回列表的列表
    unique_elements = list(map(list, unique_tuples))
    
    print(unique_elements) # 输出: [[1, 2], [3, 4], [5, 6], [3, 4, 5]] (顺序可能打乱)
    登录后复制

    如果需要保留顺序,可以结合

    OrderedDict
    登录后复制

    from collections import OrderedDict
    
    original_list_of_lists = [[1, 2], [3, 4], [1, 2], [5, 6], [3, 4, 5]]
    
    # 将内部列表转换为元组,然后用OrderedDict去重
    unique_tuples_ordered = list(OrderedDict.fromkeys(map(tuple, original_list_of_lists)))
    unique_elements_ordered = list(map(list, unique_tuples_ordered))
    
    print(unique_elements_ordered) # 输出: [[1, 2], [3, 4], [5, 6], [3, 4, 5]] (顺序保留)
    登录后复制
  • 对于字典的列表(list of dicts): 字典是不可哈希的。如果你想基于字典的内容去重,一个常见的做法是:

    1. 将字典转换为一个可哈希的表示,比如按键排序后的元组的元组(tuple of sorted key-value tuples)。
    2. 或者,将字典序列化为JSON字符串(如果字典内容复杂且有嵌套)。
    list_of_dicts = [
        {'id': 1, 'name': 'Alice'},
        {'id': 2, 'name': 'Bob'},
        {'name': 'Alice', 'id': 1}, # 这是一个重复项,但键顺序不同
        {'id': 3, 'name': 'Charlie'}
    ]
    
    seen_hashes = set()
    unique_dicts = []
    
    for d in list_of_dicts:
        # 将字典转换为可哈希的形式
        # 确保键值对的顺序一致,以便生成相同的哈希
        # 排序后的items()返回一个列表,再转为元组
        dict_hashable = tuple(sorted(d.items())) 
    
        if dict_hashable not in seen_hashes:
            seen_hashes.add(dict_hashable)
            unique_dicts.append(d)
    
    print(unique_dicts) 
    # 输出: [{'id': 1, 'name': 'Alice'}, {'id': 2, 'name': 'Bob'}, {'id': 3, 'name': 'Charlie'}]
    登录后复制

    这里我们通过

    tuple(sorted(d.items()))
    登录后复制
    将字典转换成了一个可哈希的元组,这样就可以用
    set
    登录后复制
    来快速判断是否重复了。

技巧三:自定义对象的

__hash__
登录后复制
__eq__
登录后复制
方法

如果你处理的是自定义类的实例,并且希望它们能被用于

set
登录后复制
或作为字典的键,那么你就需要在类中实现
__hash__
登录后复制
__eq__
登录后复制
方法。

class MyObject:
    def __init__(self, id, name):
        self.id = id
        self.name = name

    # 定义相等性:当id和name都相同时,两个MyObject实例被认为是相等的
    def __eq__(self, other):
        if not isinstance(other, MyObject):
            return NotImplemented
        return self.id == other.id and self.name == other.name

    # 定义哈希值:基于id和name的哈希值
    # 注意:如果两个对象相等,它们的哈希值必须相等
    def __hash__(self):
        return hash((self.id, self.name))

    def __repr__(self):
        return f"MyObject(id={self.id}, name='{self.name}')"

objects = [
    MyObject(1, 'A'),
    MyObject(2, 'B'),
    MyObject(1, 'A'), # 重复项
    MyObject(3, 'C')
]

unique_objects = list(set(objects))
print(unique_objects) # 输出: [MyObject(id=1, name='A'), MyObject(id=2, name='B'), MyObject(id=3, name='C')]
登录后复制

实现这两个魔法方法后,你的自定义对象就变得“哈希友好”了,可以和普通的可哈希对象一样,直接用

set
登录后复制
OrderedDict
登录后复制
进行去重。这是处理自定义对象去重的最“正规”也是最推荐的方式。

Python列表去重时,如何保持原有顺序?

在实际开发中,列表元素的顺序往往很重要。你可能不希望去重后,原本的排列被打乱。幸运的是,Python提供了几种既能去重又能保留原始顺序的方法。我个人在处理这类问题时,通常会在效率和代码简洁性之间做权衡。

  1. 使用

    collections.OrderedDict.fromkeys()
    登录后复制
    (推荐,简洁高效) 这是我个人最喜欢且最常使用的方法,因为它兼顾了效率和代码的优雅。
    OrderedDict
    登录后复制
    会记住元素的插入顺序,而
    fromkeys()
    登录后复制
    方法又保证了键的唯一性。

    from collections import OrderedDict
    
    my_list = ['apple', 'banana', 'orange', 'apple', 'grape', 'banana']
    unique_ordered_list = list(OrderedDict.fromkeys(my_list))
    print(unique_ordered_list) # 输出: ['apple', 'banana', 'orange', 'grape']
    登录后复制

    这个方法非常直观,一行代码就能搞定,而且底层实现基于哈希表,所以效率很高,平均时间复杂度是O(n)。这是我处理哈希able对象去重并保留顺序时的首选。

  2. 使用列表推导式与辅助

    set
    登录后复制
    (推荐,Pythonic) 这种方法稍微比
    OrderedDict
    登录后复制
    多写几行,但它同样高效且易于理解。它通过一个
    set
    登录后复制
    来记录已经见过的元素,确保只将未见过的元素添加到结果列表中。

    my_list = ['apple', 'banana', 'orange', 'apple', 'grape', 'banana']
    
    seen = set()
    unique_ordered_list = [item for item in my_list if item not in seen and not seen.add(item)]
    
    print(unique_ordered_list) # 输出: ['apple', 'banana', 'orange', 'grape']
    登录后复制

    这里的

    not seen.add(item)
    登录后复制
    是一个巧妙的用法。
    set.add()
    登录后复制
    方法返回
    None
    登录后复制
    not None
    登录后复制
    即为
    True
    登录后复制
    。所以,当
    item
    登录后复制
    不在
    seen
    登录后复制
    中时,
    item not in seen
    登录后复制
    True
    登录后复制
    seen.add(item)
    登录后复制
    被执行,并且
    not seen.add(item)
    登录后复制
    也为
    True
    登录后复制
    item
    登录后复制
    因此被添加到
    unique_ordered_list
    登录后复制
    。如果
    item
    登录后复制
    已在
    seen
    登录后复制
    中,
    item not in seen
    登录后复制
    False
    登录后复制
    ,整个条件判断短路,
    item
    登录后复制
    不会被添加。这种方式同样是O(n)的平均时间复杂度。

  3. 传统循环与辅助

    set
    登录后复制
    (易理解,但不如列表推导式简洁) 这其实是上面列表推导式方法的“展开版”,对于初学者来说可能更容易理解其逻辑。

    my_list = ['apple', 'banana', 'orange', 'apple', 'grape', 'banana']
    
    unique_ordered_list
    登录后复制

以上就是python怎么从列表中删除重复项_python列表去重高效实现方法的详细内容,更多请关注php中文网其它相关文章!

python速学教程(入门到精通)
python速学教程(入门到精通)

python怎么学习?python怎么入门?python在哪学?python怎么学才快?不用担心,这里为大家提供了python速学教程(入门到精通),有需要的小伙伴保存下载就能学习啦!

下载
来源:php中文网
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn
最新问题
开源免费商场系统广告
热门教程
更多>
最新下载
更多>
网站特效
网站源码
网站素材
前端模板
关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新 English
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号 技术交流群
PHP中文网订阅号
每天精选资源文章推送
PHP中文网APP
随时随地碎片化学习

Copyright 2014-2025 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号