python:如何使用集合从列表中删除重复项(顺序很重要)

Python: how to remove duplicates from a list using a set (order is important)

本问题已经有最佳答案,请猛点这里访问。

所以我有这个单子:a = [-11, 13, 13, 10, -11, 10, 9, -3, 6, -9, -6, -6, 13, 8, -11, -5, 6, -8, -12, 5, -9, -1, -5, 2, -2, 13, 14, -9, 7, -4]

通过使用集合,我需要删除重复项,并保持它们的顺序一致。

我用了这个代码:

1
2
3
def unique(a):
    a = set(a)
    return list(a)

当我使用它时,它确实会删除重复项,但问题是它会按如下数字顺序返回它们:

1
2
>>> unique(a)
[-2, 2, 5, 6, 7, 8, 9, 10, 13, 14, -12, -11, -9, -8, -6, -5, -4, -3, -1]

在使用集合删除重复项时,如何以与原始列表相同的顺序返回它?

编辑:

所以我使用这个代码是因为它起作用:

1
2
3
def unique(a):
    seen = set()
    return [seen.add(x) or x for x in a if x not in seen]

但是有人能给我解释一下它的作用吗?因为我需要再做一次,但它返回的列表没有负数,我不能这样做,除非我理解代码的作用。


该功能已经存在于itertools配方中,如unique_everseen中。您可以从那里复制和粘贴它,或者阅读它以了解它的工作原理,或者安装第三方软件包more-itertools并从那里使用它。

下面是代码的简化版本:

1
2
3
4
5
6
def unique_everseen(iterable):
    seen = set()
    for element in iterable:
        if element not in seen:
            seen.add(element)
            yield element

配方中的版本允许您不需要的key函数,它有两个优化。但首先要了解简单的版本:

seen是迄今为止所见的所有值的集合。对于每个值,我们检查它是否在seen中。如果是这样,我们就跳过它。否则,我们将它添加到集合和yield中。所以,我们只在第一次看到每个元素时才开始使用cx1(6)。

配方版本中的第一个优化很简单:查找seen.add方法不是很自由,所以我们通过执行seen_add = seen.add来执行一次而不是n次。当基准测试一些小的用例时,这会产生相当大的差异,比如一个小整数列表;在实际用例中,对于哈希值比较昂贵的值,这可能不会产生太大的差异。

第二个优化是使用ifilterfalse而不是if跳过已经看到的元素。基本上,这意味着如果您有n个元素和m个独特的元素,那么您只需要在python中执行m次迭代,在ifilterfalse中执行优化的C代码中执行n次迭代,而不是在python中执行n次迭代。因为在C中迭代要快得多,所以除非几乎所有元素都是唯一的,否则这是值得的。

要使它与key函数一起工作,您所要做的就是保留一组目前为止看到的key(element)值,而不是目前看到的element值。这使得ifilterfalse优化变得更难做,而且效率更低,所以它没有完成。

如果您只处理序列,而不是任意的iterables,并且您可以依靠python 2.7+,那么还有另一种方法可以做到这一点,它几乎同样有效,甚至更简单:

1
2
def unique(a):
    return OrderedDict.fromkeys(a).keys()

滥用清单理解:

1
2
3
4
def unique(seq):
    seen = set()
    return [seen.add(x) or x for x in seq if x not in seen]
    # or use parentheses instead of brackets above for a generator