保证列表中的所有元素都不同的最Pythonic方式是什么?

8

我有一个在Python中生成的列表,是程序的一部分。我非常确定这些元素都不同,并用assertion进行了检查。

目前我这样做:

如果有两个元素:

try:
    assert(x[0] != x[1])
except:
    print debug_info
    raise Exception("throw to caller")

如果有三个:
try:
    assert(x[0] != x[1])
    assert(x[0] != x[2])
    assert(x[1] != x[2])
except:
    print debug_info
    raise Exception("throw to caller")

如果我要处理四个元素的话,我会疯掉。

有没有更好的方法来确保列表中的所有元素都是唯一的?

7个回答

26

也许像这样:

if len(x) == len(set(x)):
    print "all elements are unique"
else:
    print "elements are not unique"

2
你可以一开始就将它们存储在一个集合中,以确保它们都是唯一的。或者在存储到集合中之前检查成员资格。如果您无法控制输入格式,则这绝对有效。 - Chris Lutz
集合并不一定保留顺序,而这可能是很重要的。 - Colin Coghill
2
为什么这对此目的很重要。我所尝试做的就是看看在去除重复项后,列表是否具有相同数量的元素。 - Elias Zamaria

18

最流行的答案是O(N)(很好!),但正如@Paul和@Mark指出的那样,它们要求列表的项目是可哈希的。@Paul和@Mark提出的非可哈希项的方法是通用的,但需要O(N平方)的时间复杂度,即很长时间。

如果您的列表项目不可哈希但是可比较,则可以做得更好……这里有一种方法,总是以尽可能快的速度处理列表项的特性。

import itertools

def allunique(L):
  # first try sets -- fastest, if all items are hashable
  try:
    return len(L) == len(set(L))
  except TypeError:
    pass
  # next, try sort -- second fastest, if items are comparable
  try:
    L1 = sorted(L)
  except TypeError:
    pass
  else:
    return all(len(list(g))==1 for k, g in itertools.groupby(L1))
  # fall back to the slowest but most general approach
  return all(v not in L[i+1:] for i, L in enumerate(L))

如果所有的项目都是可哈希的,那么时间复杂度为O(N),如果有些项目不可哈希但可以比较,那么时间复杂度为O(N log N)。对于一些不可避免的情况,例如某些项目是不可哈希的(例如字典),或者某些项目无法进行比较(例如复数),时间复杂度为O(N squared)。

这段代码的灵感来源于伟大的 Tim Peters 的一份旧食谱,该食谱实际上生成了一个唯一项目列表(并且也是在很久以前,set 还不存在——它必须使用 dict...!-),但基本上面临着相同的问题。


你是不是想说 v not in L[i+1:] for v,i in enumerate(L) - chepner

7
这个怎么样:
if len(x) != len(set(x)):
    raise Exception("throw to caller")

这里假设x中的元素是可哈希的。

2
希望你的序列中所有的项都是不可变的——如果不是,你将无法在序列上调用“set”方法。
>>> set( ([1,2], [3,4]) )
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: unhashable type: 'list'

如果你有可变项,你就不能哈希这些项,你基本上必须不断地检查列表:

def isUnique(lst):
    for i,v in enumerate(lst):
        if v in lst[i+1:]:
            return False
    return True

>>> isUnique( ([1,2], [3,4]) )
True
>>> isUnique( ([1,2], [3,4], [1,2]) )
False

1

在构建列表时,您可以检查值是否已经存在,例如:

if x in y:
     raise Exception("Value %s already in y" % x)
else:
     y.append(x)

这样做的好处是可以报告冲突的变量。


0
你可以处理列表以创建一个已知为唯一的副本。
def make_unique(seq): 
    t = type(seq) 
    seen = set()
    return t(c for c in seq if not (c in seen or seen.add(c)))

或者如果序列元素不可哈希:

def unique1(seq):
    t = type(seq) 
    seen = [] 
    return t(c for c in seq if not (c in seen or seen.append(c)))

这将保持项目的顺序(当然,省略重复项)。


0
我会使用这个:
mylist = [1,2,3,4]
is_unique = all(mylist.count(x) == 1 for x in mylist)

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