所以我有这个单子: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  | 
 
		
		
- seen.add总是返回None,所以这不起作用。
 
- 固定,表示or。
 
- 编辑之后,它确实起作用了,但它仍然很可怕。在一个表达式中使用or对两个操作进行排序甚至比使用列表理解的副作用更是一种滥用。
 
- 是的,当然是!
 
- 实际上,只需将add置于条件:[x for x in seq if x not in seen and not seen.add(x)],就可以消除listcomp的滥用。但这仍然是对seen.add的滥用,而且可能更难看到这种情况……
 
- 我觉得set真的可以使用一个方法来添加一个项,并返回一个布尔值来指示该项是否确实需要添加。那么你就可以这样做,比如说[x for x in seq if seen.did_add(x)]。
 
- 几乎没有一种内置类型的方法会发生变化,但是返回一个值……但是少数例外之一,dict.setdefault与您建议的set.did_add相距不远,所以这可能是合理的。