在Python列表上执行排序和去重,最干净的方法是什么?

88
考虑一个包含['foo', 'foo', 'bar']的Python列表my_list

最Pythonic的方法是什么?去重并排序一个列表?
(类似于cat my_list | sort | uniq)

这是我目前的做法,虽然它可以工作,但我相信有更好的方法。

my_list = []
...
my_list.append("foo")
my_list.append("foo")
my_list.append("bar")
...
my_list = set(my_list)
my_list = list(my_list)
my_list.sort()

可能是重复的问题:如何从Python列表中删除重复项并保持顺序? - user82216
5个回答

143
my_list = sorted(set(my_list))

27
请注意,这仅适用于可哈希类型,因此例如无法使用列表进行此操作。 - taleinat
不是的,区别在于 sort() 方法是原地排序,因此不需要额外的分配空间。 - Ignacio Vazquez-Abrams
不是Python的,如果有混淆的话。我的观点是OP的代码(cat my_list | sort | uniq)将在适合硬盘的文件上运行,而您的代码将需要适合RAM。 - Reut Sharabani
2
一个排序后的原地去重操作比将列表转换为集合,然后对其进行排序要高效得多。即使使用最小堆也是更可取的。 - ioquatix
优雅,但是Pylint更喜欢使用花括号的集合语法而不是set函数:R1718: 考虑使用集合推导式 - undefined
显示剩余7条评论

20
# Python ≥ 2.4
# because of (generator expression) and itertools.groupby, sorted

import itertools

def sort_uniq(sequence):
    return (x[0] for x in itertools.groupby(sorted(sequence)))
更快:
import itertools, operator
import sys

if sys.hexversion < 0x03000000:
    mapper= itertools.imap # 2.4 ≤ Python < 3
else:
    mapper= map # Python ≥ 3

def sort_uniq(sequence):
    return mapper(
        operator.itemgetter(0),
        itertools.groupby(sorted(sequence)))

两个版本都返回生成器,因此您可能希望将结果提供给列表类型:

sequence= list(sort_uniq(sequence))

请注意,这也适用于不可哈希的项目:

>>> list(sort_uniq([[0],[1],[0]]))
[[0], [1]]

1
如果您正在使用Python3:Py3中的map和Py2中的itertools.imap执行完全相同的操作。(在Py3中,iter(map(...))是多余的。) - The Demz
如果你有大量的数据,这比被接受的答案要好得多。+1 - Reut Sharabani
@TheDemz,需要考虑到Python 3现在比以前更为普遍;谢谢。 - tzot
请注意,如果您使用key参数来决定元素之间的某种替代相等性以实现唯一性目的(大致相当于将-f-s用作uniq的参数),那么x [0](或operator.itemgetter(0))将无法正常工作。在这种情况下,关键字与输入数据元素不同。我认为,在这种情况下,类似next(iter(x[1]))的东西可以解决每个“根据关键函数相同”的组的第一个元素。 - Robie Basak

8
简单直接的解决方案由Ignacio提供:sorted(set(foo))
如果您有唯一的数据,您可能不仅想做sorted(set(...)),而是始终存储一个集合,并偶尔提取值的排序版本。(此时,它开始听起来像人们经常使用数据库的那种东西。)
如果您有一个排序过的列表,并且希望以对数时间检查成员资格并在最坏情况下线性时间添加项目,则可以使用bisect模块
如果您想始终保持此条件,并且想简化事物或使某些操作执行得更好,您可以考虑blist.sortedset

1
考虑使用sortedcontainers。与blist相比,SortedSet更快且纯Python实现。详见性能测试 - GrantJ

2

其他人已经提到了sorted(set(my_list)),对于可哈希的值(如字符串、数字和元组)可以工作,但是对于不可哈希的类型(如列表)则无法使用。

为了获得任何可排序类型的已排序且没有重复项的值列表:

from itertools import izip, islice
def unique_sorted(values):
    "Return a sorted list of the given values, without duplicates."
    values = sorted(values)
    if not values:
        return []
    consecutive_pairs = izip(values, islice(values, 1, len(values)))
    result = [a for (a, b) in consecutive_pairs if a != b]
    result.append(values[-1])
    return result

这可以进一步简化,使用“pairwise”或“unique_justseen”配方来自itertools文档

-4

不能说这是一种干净的方法,但只是为了好玩:

my_list = [x for x in sorted(my_list) if not x in locals()["_[1]"]]

当然,这只是为了好玩,正如我所指出的。 - andreypopp

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接