0

0

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

冰火之心

冰火之心

发布时间:2025-09-13 09:41:01

|

911人浏览过

|

来源于php中文网

原创

最直接去重方法是使用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()
就无法满足你的需求了。

钛投标
钛投标

钛投标 | 全年免费 | 不限字数 | AI标书智写工具

下载

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

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

举个例子:

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

热门AI工具

更多
DeepSeek
DeepSeek

幻方量化公司旗下的开源大模型平台

豆包大模型
豆包大模型

字节跳动自主研发的一系列大型语言模型

通义千问
通义千问

阿里巴巴推出的全能AI助手

腾讯元宝
腾讯元宝

腾讯混元平台推出的AI助手

文心一言
文心一言

文心一言是百度开发的AI聊天机器人,通过对话可以生成各种形式的内容。

讯飞写作
讯飞写作

基于讯飞星火大模型的AI写作工具,可以快速生成新闻稿件、品宣文案、工作总结、心得体会等各种文文稿

即梦AI
即梦AI

一站式AI创作平台,免费AI图片和视频生成。

ChatGPT
ChatGPT

最最强大的AI聊天机器人程序,ChatGPT不单是聊天机器人,还能进行撰写邮件、视频脚本、文案、翻译、代码等任务。

相关专题

更多
数据类型有哪几种
数据类型有哪几种

数据类型有整型、浮点型、字符型、字符串型、布尔型、数组、结构体和枚举等。本专题为大家提供相关的文章、下载、课程内容,供大家免费下载体验。

335

2023.10.31

php数据类型
php数据类型

本专题整合了php数据类型相关内容,阅读专题下面的文章了解更多详细内容。

223

2025.10.31

c语言 数据类型
c语言 数据类型

本专题整合了c语言数据类型相关内容,阅读专题下面的文章了解更多详细内容。

138

2026.02.12

js 字符串转数组
js 字符串转数组

js字符串转数组的方法:1、使用“split()”方法;2、使用“Array.from()”方法;3、使用for循环遍历;4、使用“Array.split()”方法。本专题为大家提供js字符串转数组的相关的文章、下载、课程内容,供大家免费下载体验。

738

2023.08.03

js截取字符串的方法
js截取字符串的方法

js截取字符串的方法有substring()方法、substr()方法、slice()方法、split()方法和slice()方法。本专题为大家提供字符串相关的文章、下载、课程内容,供大家免费下载体验。

219

2023.09.04

java基础知识汇总
java基础知识汇总

java基础知识有Java的历史和特点、Java的开发环境、Java的基本数据类型、变量和常量、运算符和表达式、控制语句、数组和字符串等等知识点。想要知道更多关于java基础知识的朋友,请阅读本专题下面的的有关文章,欢迎大家来php中文网学习。

1561

2023.10.24

字符串介绍
字符串介绍

字符串是一种数据类型,它可以是任何文本,包括字母、数字、符号等。字符串可以由不同的字符组成,例如空格、标点符号、数字等。在编程中,字符串通常用引号括起来,如单引号、双引号或反引号。想了解更多字符串的相关内容,可以阅读本专题下面的文章。

649

2023.11.24

java读取文件转成字符串的方法
java读取文件转成字符串的方法

Java8引入了新的文件I/O API,使用java.nio.file.Files类读取文件内容更加方便。对于较旧版本的Java,可以使用java.io.FileReader和java.io.BufferedReader来读取文件。在这些方法中,你需要将文件路径替换为你的实际文件路径,并且可能需要处理可能的IOException异常。想了解更多java的相关内容,可以阅读本专题下面的文章。

1188

2024.03.22

JavaScript浏览器渲染机制与前端性能优化实践
JavaScript浏览器渲染机制与前端性能优化实践

本专题围绕 JavaScript 在浏览器中的执行与渲染机制展开,系统讲解 DOM 构建、CSSOM 解析、重排与重绘原理,以及关键渲染路径优化方法。内容涵盖事件循环机制、异步任务调度、资源加载优化、代码拆分与懒加载等性能优化策略。通过真实前端项目案例,帮助开发者理解浏览器底层工作原理,并掌握提升网页加载速度与交互体验的实用技巧。

44

2026.03.06

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
最新Python教程 从入门到精通
最新Python教程 从入门到精通

共4课时 | 22.5万人学习

Django 教程
Django 教程

共28课时 | 4.8万人学习

SciPy 教程
SciPy 教程

共10课时 | 1.8万人学习

关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号 技术交流群
PHP中文网订阅号
每天精选资源文章推送

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