如何在保持秩序的同时从列表中删除重复项?
有没有一种内置的方法可以从Python中的列表中删除重复项,同时保持顺序? 我知道我可以使用一套删除重复项,但破坏了原来的顺序。 我也知道我可以像这样推出自己的产品:
def uniq(input):
output = []
for x in input:
if x not in output:
output.append(x)
return output
(感谢解放这个代码示例。)
但是,如果可能的话,我想利用内置的或更加Pythonic的成语。
相关问题:在Python中,从列表中删除重复项的最快算法是什么,以便所有元素都是唯一的,同时保持顺序?
在这里你有一些选择:http://www.peterbe.com/plog/uniqifiers-benchmark
最快的一个:
def f7(seq):
seen = set()
seen_add = seen.add
return [x for x in seq if not (x in seen or seen_add(x))]
为什么将seen.add
分配给seen_add
而不是只调用seen.add
? Python是一种动态语言,可以解决seen.add
每次迭代都比解析局部变量更昂贵。 seen.add
在迭代之间可能会发生变化,并且运行时不够聪明以排除这种情况。 为了安全起见,它必须每次检查对象。
如果你打算在同一个数据集上使用这个函数,也许你会更好地使用一个有序集合:http://code.activestate.com/recipes/528878/
O(1)每个操作的插入,删除和成员检查。
编辑2016年
正如Raymond指出的那样,在OrderedDict
在C中实现的python 3.5+中,列表理解方法将比OrderedDict
慢(除非实际上最后需要列表 - 即使这样,只有在输入非常短的情况下)。 所以3.5+的最佳解决方案是OrderedDict
。
重要编辑2015年
正如@abarnert所指出的, more_itertools
库( pip install more_itertools
)包含一个unique_everseen
函数,它可以解决这个问题,而列表not seen.add
没有任何不可读的 ( not seen.add
) 突变 。 这也是最快的解决方案:
>>> from more_itertools import unique_everseen
>>> items = [1, 2, 0, 1, 3, 2]
>>> list(unique_everseen(items))
[1, 2, 0, 3]
只需一个简单的库导入,不需要黑客入侵。 这来自itertools配方unique_everseen
的实现,它看起来像:
def unique_everseen(iterable, key=None):
"List unique elements, preserving order. Remember all elements ever seen."
# unique_everseen('AAAABBBCCDAABBB') --> A B C D
# unique_everseen('ABBCcAD', str.lower) --> A B C D
seen = set()
seen_add = seen.add
if key is None:
for element in filterfalse(seen.__contains__, iterable):
seen_add(element)
yield element
else:
for element in iterable:
k = key(element)
if k not in seen:
seen_add(k)
yield element
在Python 2.7+
,接受的常用成语(它的工作原理,但并未针对速度进行优化,现在我将使用unique_everseen
)为此使用collections.OrderedDict
:
运行时间: O(N)
>>> from collections import OrderedDict
>>> items = [1, 2, 0, 1, 3, 2]
>>> list(OrderedDict.fromkeys(items))
[1, 2, 0, 3]
这看起来比以下更好:
seen = set()
[x for x in seq if x not in seen and not seen.add(x)]
并没有利用丑陋的黑客 :
not seen.add(x)
这依赖于set.add
是一个始终返回None
的就地方法,因此not None
评估为True
。
但请注意,尽管它具有相同的运行时复杂度O(N),但解决方案在原始速度上更快。
在Python 2.7中 ,从迭代中移除重复的新方法同时保持原始顺序:
>>> from collections import OrderedDict
>>> list(OrderedDict.fromkeys('abracadabra'))
['a', 'b', 'r', 'c', 'd']
在Python 3.5中 ,OrderedDict有一个C实现。 我的计时表明,现在这是Python 3.5各种方法中速度最快,最短的一种。
在Python 3.6中 ,常规字典变得既有序又紧凑。 (这个特性适用于CPython和PyPy,但在其他实现中可能不存在)。 这为我们提供了一种新的最快捷的重复数据删除方式,同时保留订单
>>> list(dict.fromkeys('abracadabra'))
['a', 'b', 'r', 'c', 'd']
在Python 3.7中 ,常规字典保证在所有实现中都有序。 所以,最短和最快的解决方案是:
>>> list(dict.fromkeys('abracadabra'))
['a', 'b', 'r', 'c', 'd']
对@max的回应:一旦你移动到3.6或3.7,并使用常规词典而不是OrderedDict,你无法以其他方式真正击败游戏。 字典很密集,很容易转换成列表,几乎没有开销。 目标列表预先调整为len(d),该列表保存在列表理解中发生的所有调整大小。 另外,由于内部密钥列表是密集的,所以复制指针几乎快速地作为列表副本。
链接地址: http://www.djcxy.com/p/18773.html上一篇: How do you remove duplicates from a list whilst preserving order?