所以我有这个清单: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]
通过使用一组,我需要删除重复项并使它们保持相同的顺序
我使用了这段代码:
def unique(a):
a = set(a)
return list(a)
Run Code Online (Sandbox Code Playgroud)
当我使用它时,它确实会删除重复项,但问题是它按数字顺序返回它们,如下所示:
>>> unique(a)
[-2, 2, 5, 6, 7, 8, 9, 10, 13, 14, -12, -11, -9, -8, -6, -5, -4, -3, -1]
Run Code Online (Sandbox Code Playgroud)
如何以与原始列表相同的顺序返回它,同时使用集合删除重复项?
编辑:
所以我使用了这段代码,因为它有效:
def unique(a):
seen = set()
return [seen.add(x) or x for x in a if x not in seen]
Run Code Online (Sandbox Code Playgroud)
但有人可以向我解释它的作用吗?因为我需要再做一次,但它返回不带负数的列表,除非我理解该代码的作用,否则我无法这样做
该功能已经存在于itertools食谱中,如unique_everseen。您可以从那里复制并粘贴它,或者阅读它以查看它是如何工作的,或者安装第三方软件包more-itertools并从那里使用它。
这是代码的简化版本:
def unique_everseen(iterable):
seen = set()
for element in iterable:
if element not in seen:
seen.add(element)
yield element
Run Code Online (Sandbox Code Playgroud)
食谱中的版本允许key您不需要的功能,并且它有两个优化。但首先了解简单的版本:
seen是迄今为止看到的所有值的集合。对于每个值,我们检查它是否在seen. 如果是这样,我们就跳过它。否则,我们将其添加到集合中yield。所以,我们yield只是第一次看到每个元素。
菜谱版本中的第一个优化很简单:查找seen.add方法不是很免费,因此我们通过执行 来执行一次而不是 N 次seen_add = seen.add。当对小整数列表等琐碎情况进行基准测试时,这会产生相当大的差异;在实际用例中,对于散列成本更高的值,它可能不会产生太大影响。
第二个优化是使用ifilterfalse而不是if跳过已经见过的元素。基本上这意味着,如果你有 N 个元素和 M 个唯一元素,你只需要在 Python 中进行 M 次迭代,并在内部优化的 C 代码中进行 N 次迭代ifilterfalse,而不是在 Python 中进行 N 次迭代。由于 C 中的迭代速度要快得多,因此这是值得的,除非几乎所有元素都是唯一的。
要使其与key函数一起工作,您所要做的就是保留一组key(element)到目前为止看到的值,而不是element到目前为止看到的值。这使得ifilterfalse优化变得有点困难,而且效率也低得多,所以没有完成。
如果您只处理序列,而不是任意迭代,并且可以依靠 Python 2.7+,那么还有另一种方法可以做到这一点,它几乎同样高效,甚至更简单:
def unique(a):
return OrderedDict.fromkeys(a).keys()
Run Code Online (Sandbox Code Playgroud)