Python怎么把列表中的所有元素去重_Python列表去重技巧与方法

冰火之心
发布: 2025-09-13 09:41:01
原创
884人浏览过
最直接去重方法是使用set(),但会丢失顺序;若需保留顺序且元素可哈希,推荐dict.fromkeys();对于不可哈希元素或复杂结构,应采用手动迭代结合辅助集合的方式。

python怎么把列表中的所有元素去重_python列表去重技巧与方法

Python中要将列表中的所有元素去重,最直接也最常用的方法是利用

set
登录后复制
(集合)的数据结构特性,因为集合天生就是不包含重复元素的。如果你需要保持元素的原始顺序,那么可以考虑使用
dict.fromkeys()
登录后复制
方法(Python 3.7+)或者结合循环与辅助集合来实现。每种方法都有其适用场景和性能考量,没有绝对的“最佳”,只有最适合你当前需求的选择。

解决方案

在Python中处理列表去重,我通常会根据几个关键因素来选择方法:原始顺序是否重要?列表中的元素是否都是可哈希的?以及对性能的要求有多高?

1. 最简洁高效:利用

set()
登录后复制
进行去重

这是我最常推荐的方法,因为它极其简洁且效率高,尤其适用于元素顺序无关紧要的场景。

set
登录后复制
是Python内置的一种数据结构,它只存储唯一的元素。

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

my_list = [1, 2, 2, 3, 4, 4, 5, 'a', 'b', 'a']
unique_list_unordered = list(set(my_list))
print(unique_list_unordered)
# 可能会输出类似:[1, 2, 3, 4, 5, 'a', 'b'],顺序不确定
登录后复制

这种方法的核心在于

set()
登录后复制
会自动过滤掉重复项,然后我们再用
list()
登录后复制
将其转换回列表。它的优点是代码量少,执行速度快,因为它内部使用了哈希表来实现快速查找和去重。但缺点也很明显:它会丢失原始列表中元素的顺序。

2. 兼顾效率与顺序:利用

dict.fromkeys()
登录后复制

如果你既想去重又想保留原始元素的插入顺序,那么从Python 3.7开始,

dict.fromkeys()
登录后复制
是一个非常优雅且高效的选择。这是因为从Python 3.7起,字典会保持元素的插入顺序。

my_list = [1, 2, 2, 3, 4, 4, 5, 'a', 'b', 'a']
unique_list_ordered = list(dict.fromkeys(my_list))
print(unique_list_ordered)
# 输出:[1, 2, 3, 4, 5, 'a', 'b']
登录后复制

dict.fromkeys(iterable)
登录后复制
会创建一个新的字典,其中
iterable
登录后复制
中的元素作为键,值默认为
None
登录后复制
。由于字典的键必须是唯一的,这自然就实现了去重。然后,我们再将其转换回列表。这种方法在大多数情况下,是保留顺序去重的最佳实践。

对于Python 3.6及更早版本,如果你需要保留顺序,可以使用

collections.OrderedDict
登录后复制

from collections import OrderedDict

my_list = [1, 2, 2, 3, 4, 4, 5, 'a', 'b', 'a']
unique_list_ordered_old_python = list(OrderedDict.fromkeys(my_list))
print(unique_list_ordered_old_python)
# 输出:[1, 2, 3, 4, 5, 'a', 'b']
登录后复制

3. 手动迭代与辅助集合:最灵活但稍显繁琐

当列表中的元素包含不可哈希类型(如列表、字典本身)时,或者你需要更精细的控制逻辑时,基于循环和辅助集合的方法就派上用场了。

my_list = [1, 2, [3, 4], 2, [3, 4], 5, {'a': 1}, {'a': 1}] # 包含不可哈希元素

unique_list = []
seen = set() # 用于存储已见过的、可哈希的元素
for item in my_list:
    # 对于可哈希元素,直接用set判断
    if isinstance(item, (int, str, float, tuple)): # 假设这些是可哈希的
        if item not in seen:
            unique_list.append(item)
            seen.add(item)
    else: # 对于不可哈希元素(如列表、字典),需要特殊处理
        # 这里的逻辑会比较复杂,取决于你如何定义“重复”
        # 比如,对于字典,你可以比较特定键的值
        # 对于列表,你可以将其转换为元组再比较
        # 示例:假设我们想去重字典,根据其'a'键的值
        if isinstance(item, dict) and 'a' in item:
            item_id = item['a']
            if item_id not in seen:
                unique_list.append(item)
                seen.add(item_id) # 记录的是键的值,而不是字典本身
        elif isinstance(item, list):
            # 将列表转换为元组进行哈希和比较
            item_tuple = tuple(item)
            if item_tuple not in seen:
                unique_list.append(item)
                seen.add(item_tuple)
        else: # 其他不可哈希类型,直接添加(或者根据业务逻辑处理)
            # 这部分需要根据实际需求来定,这里只是一个示例
            if item not in unique_list: # 这种判断效率较低,O(N)
                 unique_list.append(item)

print(unique_list)
# 示例输出(取决于具体逻辑):[1, 2, [3, 4], 5, {'a': 1}]
登录后复制

这个方法虽然看起来复杂,但它的优势在于灵活性。你可以完全控制如何定义“重复”,尤其是在处理嵌套列表、字典或自定义对象时,这往往是唯一的出路。

seen
登录后复制
集合在这里起到了关键的优化作用,将每次
in
登录后复制
操作的平均时间复杂度从O(N)降低到O(1)。


为什么直接使用
set()
登录后复制
可能会“不尽人意”?

使用

set()
登录后复制
进行列表去重,虽然在代码简洁性和执行效率上表现出色,但它并非万能,有时甚至会“不尽人意”,这主要体现在两个方面:

首先,也是最明显的一点,

set()
登录后复制
会丢失元素的原始顺序。集合的数学定义本身就是无序的,Python的
set
登录后复制
也遵循这一特性。当你将一个列表转换成集合再转换回列表时,元素的排列顺序是不可预测的,这对于那些依赖于元素出现先后顺序的场景来说,是不可接受的。比如,你有一个用户操作日志列表,去重后你还想知道用户第一次执行某个操作的顺序,那么
set()
登录后复制
就无法满足你的需求了。

其次,也是一个更深层次的限制,

set()
登录后复制
只能处理可哈希(hashable)的元素。在Python中,可哈希意味着一个对象的哈希值在其生命周期内是不可变的,并且可以与其他对象进行比较。通常,数字、字符串、元组(如果其所有元素都是可哈希的)都是可哈希的。然而,列表(
list
登录后复制
)和字典(
dict
登录后复制
)是不可哈希的
。这意味着,如果你的列表中包含嵌套的列表或字典,直接尝试
set(my_list)
登录后复制
会抛出
TypeError: unhashable type: 'list'
登录后复制
'dict'
登录后复制
的错误。

降重鸟
降重鸟

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

降重鸟 113
查看详情 降重鸟

举个例子:

my_list_with_lists = [1, 2, [3, 4], 2, [3, 4]]
# unique_list = list(set(my_list_with_lists)) # 这行代码会报错!
# TypeError: unhashable type: 'list'
登录后复制

在这种情况下,

set()
登录后复制
就显得力不从心了。你不能简单地把包含列表或字典的列表扔进集合里去重,因为它不知道如何计算这些不可变对象的哈希值来判断唯一性。这需要我们采用更复杂的策略,比如将不可哈希的元素转换为可哈希的形式,或者进行自定义的比较。


面对复杂数据类型,Python列表去重有哪些“变通之法”?

当列表中的元素不再是简单的数字或字符串,而是嵌套的列表、字典,或者是自定义对象时,去重就变得有挑战性了。

set()
登录后复制
的局限性在这里暴露无遗。这时候,我们就需要一些“变通之法”来应对。

1. 将不可哈希元素转换为可哈希形式

对于包含列表的列表,如果内部列表的顺序和内容决定了其“唯一性”,我们可以将其转换为元组。元组是不可变的,因此是可哈希的。

list_of_lists = [[1, 2], [3, 4], [1, 2], [5, 6], [4, 3]]
# 将每个内部列表转换为元组,然后用set去重
unique_tuples = set(tuple(item) for item in list_of_lists)
unique_list_of_lists = [list(item) for item in unique_tuples]
print(unique_list_of_lists)
# 输出:[[1, 2], [3, 4], [5, 6], [4, 3]] (顺序不保证)
登录后复制

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

dict.fromkeys()
登录后复制

unique_list_of_lists_ordered = [list(item) for item in dict.fromkeys(tuple(item) for item in list_of_lists)]
print(unique_list_of_lists_ordered)
# 输出:[[1, 2], [3, 4], [5, 6], [4, 3]]
登录后复制

对于包含字典的列表,情况会更复杂一些,因为字典的键值对顺序通常不重要,但其内容定义了唯一性。如果字典内部的键值对也是可哈希的,可以将其转换为

frozenset
登录后复制
(不可变的集合),然后去重。但更常见的是,我们根据字典中的某个或某几个特定键的值来判断唯一性。

list_of_dicts = [
    {'id': 1, 'name': 'Alice', 'age': 30},
    {'id': 2, 'name': 'Bob', 'age': 25},
    {'id': 1, 'name': 'Alice', 'age': 31}, # id为1,但age不同
    {'id': 3, 'name': 'Charlie', 'age': 35},
    {'id': 2, 'name': 'Bob', 'age': 25} # id为2,name和age都相同
]

# 策略1:根据某个唯一标识键(如'id')去重
unique_by_id = []
seen_ids = set()
for d in list_of_dicts:
    if d['id'] not in seen_ids:
        unique_by_id.append(d)
        seen_ids.add(d['id'])
print("按ID去重:", unique_by_id)
# 输出:[{'id': 1, 'name': 'Alice', 'age': 30}, {'id': 2, 'name': 'Bob', 'age': 25}, {'id': 3, 'name': 'Charlie', 'age': 35}]

# 策略2:如果整个字典的内容(键值对)都相同才算重复
# 可以将字典的items()转换为frozenset(如果值都是可哈希的)
unique_by_content = []
seen_contents = set()
for d in list_of_dicts:
    # frozenset(d.items()) 要求字典的值也是可哈希的
    # 如果值是列表或字典,这里会报错,需要进一步处理
    dict_content_hashable = frozenset(d.items())
    if dict_content_hashable not in seen_contents:
        unique_by_content.append(d)
        seen_contents.add(dict_content_hashable)
print("按内容去重:", unique_by_content)
# 输出:[{'id': 1, 'name': 'Alice', 'age': 30}, {'id': 2, 'name': 'Bob', 'age': 25}, {'id': 1, 'name': 'Alice', 'age': 31}, {'id': 3, 'name': 'Charlie', 'age': 35}]
# 注意:这里id=1的两个字典被认为是不同的,因为age不同
登录后复制

这种方法要求我们明确如何定义“重复”,并根据这个定义来构造一个可哈希的“指纹”。

2. 自定义比较函数(迭代法)

当上述方法都无法满足需求,或者元素类型非常复杂,难以转换为统一的可哈希形式时,我们可能需要退回到最原始的迭代方法,并编写自定义的比较逻辑。这种方法通常涉及一个嵌套循环,但我们可以通过一个辅助集合来优化性能。

class MyCustomObject:
    def __init__(self, id, value):
        self.id = id
        self.value = value

    # 如果要让set/dict.fromkeys直接去重,需要实现__hash__和__eq__
    # 但这里我们假设没有实现,或者需要更复杂的去重逻辑
    def __repr__(self):
        return f"MyCustomObject(id={self.id}, value='{self.value}')"

list_of_objects = [
    MyCustomObject(1, 'A'),
    MyCustomObject(2, 'B'),
    MyCustomObject(1, 'C'), # ID相同,但value不同
    MyCustomObject(3, 'D'),
    MyCustomObject(2, 'B') # ID和value都相同
]

unique_objects = []
seen_identifiers = set() # 存储用于判断唯一性的标识符

for obj in list_of_objects:
    # 假设我们认为只要id相同就认为是重复的
    identifier = obj.id
    if identifier not in seen_identifiers:
        unique_objects.append(obj)
        seen_identifiers.add(identifier)

print("按ID去重自定义对象:", unique_objects)
# 输出:[MyCustomObject(id=1, value='A'), MyCustomObject(id=2, value='B'), MyCustomObject(id=3, value='D')]
登录后复制

这种方法赋予了我们最大的控制权,能够处理几乎所有复杂的去重场景。关键在于如何设计

identifier
登录后复制
以及如何判断
item not in seen_identifiers
登录后复制
的逻辑。如果
identifier
登录后复制
本身是不可哈希的,那就不能用
set
登录后复制
来存储
seen_identifiers
登录后复制
了,可能需要一个列表,但这样会牺牲性能。


性能考量:何时选择哪种去重方法更“明智”?

在Python编程中,选择合适的去重方法不仅仅是实现功能,更要考虑其在不同场景下的性能表现。一个“明智”的选择,往往是在功能正确的前提下,兼顾时间复杂度和空间复杂度。

1.

set()
登录后复制
转换法(
list(set(my_list))
登录后复制

  • 性能特点: 平均时间复杂度为 O(N),其中N是列表的长度。这是因为集合的添加和查找操作平均都是O(1)。空间复杂度也是O(N),因为需要创建一个新的集合来存储所有唯一元素。
  • 何时明智:
    • 元素顺序无关紧要:这是最重要的前提。
    • 列表元素都是可哈希的:如果列表包含不可哈希的元素,这种方法根本无法使用。
    • 追求极致简洁和效率:对于大量数据,如果顺序不是问题,
      set()
      登录后复制
      通常是最快的选择。

2.

dict.fromkeys()
登录后复制
法(
list(dict.fromkeys(my_list))
登录后复制

  • 性能特点: 平均时间复杂度同样为 O(N),因为字典的键添加和查找操作平均也是O(1)。空间复杂度同样是O(N),需要创建一个新的字典。
  • 何时明智:
    • 需要保留原始插入顺序:这是它与
      set()
      登录后复制
      最大的区别和优势。
    • 列表元素都是可哈希的:与
      set()
      登录后复制
      一样,字典的键也必须是可哈希的。
    • Python 3.7+ 环境:如果你的Python版本较旧,可能需要使用`collections

以上就是Python怎么把列表中的所有元素去重_Python列表去重技巧与方法的详细内容,更多请关注php中文网其它相关文章!

最佳 Windows 性能的顶级免费优化软件
最佳 Windows 性能的顶级免费优化软件

每个人都需要一台速度更快、更稳定的 PC。随着时间的推移,垃圾文件、旧注册表数据和不必要的后台进程会占用资源并降低性能。幸运的是,许多工具可以让 Windows 保持平稳运行。

下载
来源: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号