协慌网

登录 贡献 社区

从 python 列表中获取唯一值

我想从以下列表中获取唯一值:

['nowplaying', 'PBS', 'PBS', 'nowplaying', 'job', 'debate', 'thenandnow']

我需要的输出是:

['nowplaying', 'PBS', 'job', 'debate', 'thenandnow']

此代码有效:

output = []
for x in trends:
    if x not in output:
        output.append(x)
print(output)

我应该使用更好的解决方案吗?

答案

首先正确声明您的列表,以逗号分隔。您可以通过将列表转换为一组来获得唯一值。

mylist = ['nowplaying', 'PBS', 'PBS', 'nowplaying', 'job', 'debate', 'thenandnow']
myset = set(mylist)
print(myset)

如果进一步将其用作列表,则应执行以下操作将其转换回列表:

mynewlist = list(myset)

另一种可能(可能更快)的可能性是从头开始使用集合而不是列表。然后您的代码应为:

output = set()
for x in trends:
    output.add(x)
print(output)

正如已经指出的那样,集合不保持原始顺序。如果需要,则应查找有序集合实现(有关更多信息,请参见此问题 )。

为了与我将使用的类型保持一致:

mylist = list(set(mylist))

如果我们需要保持元素顺序,该如何做:

used = set()
mylist = [u'nowplaying', u'PBS', u'PBS', u'nowplaying', u'job', u'debate', u'thenandnow']
unique = [x for x in mylist if x not in used and (used.add(x) or True)]

还有一个使用reduce且没有临时used var 的解决方案。

mylist = [u'nowplaying', u'PBS', u'PBS', u'nowplaying', u'job', u'debate', u'thenandnow']
unique = reduce(lambda l, x: l.append(x) or l if x not in l else l, mylist, [])

更新 - 2019 年 3 月

还有第三个解决方案,这是一个很好的解决方案,但是由于.index为 O(n)有点慢。

mylist = [u'nowplaying', u'PBS', u'PBS', u'nowplaying', u'job', u'debate', u'thenandnow']
unique = [x for i, x in enumerate(mylist) if i == mylist.index(x)]

更新 - 2016 年 10 月

另一个带有reduce解决方案,但是这次没有.append ,这使它更易于阅读和理解。

mylist = [u'nowplaying', u'PBS', u'PBS', u'nowplaying', u'job', u'debate', u'thenandnow']
unique = reduce(lambda l, x: l+[x] if x not in l else l, mylist, [])
#which can also be writed as:
unique = reduce(lambda l, x: l if x in l else l+[x], mylist, [])

注意:请记住,我们得到的文字更加可读,脚本的性能更差。

import timeit

setup = "mylist = [u'nowplaying', u'PBS', u'PBS', u'nowplaying', u'job', u'debate', u'thenandnow']"

#10x to Michael for pointing out that we can get faster with set()
timeit.timeit('[x for x in mylist if x not in used and (used.add(x) or True)]', setup='used = set();'+setup)
0.4188511371612549

timeit.timeit('[x for x in mylist if x not in used and (used.append(x) or True)]', setup='used = [];'+setup)
0.6157128810882568

timeit.timeit('reduce(lambda l, x: l.append(x) or l if x not in l else l, mylist, [])', setup=setup)
1.8778090476989746

timeit.timeit('reduce(lambda l, x: l+[x] if x not in l else l, mylist, [])', setup=setup)
2.13108491897583

timeit.timeit('reduce(lambda l, x: l if x in l else l+[x], mylist, [])', setup=setup)
2.207760810852051

timeit.timeit('[x for i, x in enumerate(mylist) if i == mylist.index(x)]', setup=setup)
2.3621110916137695

回应评论

因为@monica问了一个很好的问题:“这是如何工作的?”。对于每个遇到困难的人。我将尝试对此进行更深入的解释,以及在这里发生了什么魔术;)

所以她首先问:

我试着理解为什么为什么unique = [used.append(x) for x in mylist if x not in used]

好吧,实际上在工作

>>> used = []
>>> mylist = [u'nowplaying', u'PBS', u'PBS', u'nowplaying', u'job', u'debate', u'thenandnow']
>>> unique = [used.append(x) for x in mylist if x not in used]
>>> print used
[u'nowplaying', u'PBS', u'job', u'debate', u'thenandnow']
>>> print unique
[None, None, None, None, None]

问题在于我们只是在unique变量内部没有获得期望的结果,而只是在used变量内部。这是因为在列表理解期间.append修改了used变量并返回None

因此,为了将结果放入unique变量中,并且.append(x) if x not in used仍与.append(x) if x not in used保持相同的逻辑,我们需要将此.append调用移至列表.append的右侧,并仅返回x在左手边。

但是,如果我们太天真了,那就去:

>>> unique = [x for x in mylist if x not in used and used.append(x)]
>>> print unique
[]

我们将一无所获。

同样,这是因为.append方法返回None ,这使我们的逻辑表达式具有以下外观:

x not in used and None

这基本上总是:

  1. 计算结果为False时, xused
  2. used x时,结果为None

并且在两种情况下( False / None ),这都将被视为falsy值,结果将得到一个空列表。

但是,为什么在不used x时将其评估为None呢?有人会问。

嗯,因为这就是 Python 的短路运算符的工作方式

表达式x and y首先计算 x;如果 x 为假,则返回其值;否则,将评估 y 并返回结果值。

因此,当不使用x(即,当它为True ,下一部分或表达式将被求值used.append(x) ,并将返回其值None

但这就是我们想要的,以便从具有重复项的列表中获取唯一元素,我们希望仅在遇到第一时间时才将其.append到新列表中。

所以,我们真的要评估used.append(x)只有当xused ,也许如果把这个办法None值转换成truthy一个我们会好起来的,对不对?

好吧,是的,这是第二种short-circuit操作器发挥作用的地方。

表达式x or y首先计算 x; 如果 x 为 true,则返回其值;否则,将评估 y 并返回结果值。

我们知道.append(x)永远都是falsy ,因此,如果我们仅在他旁边添加一个or旁边,我们将始终得到下一个部分。这就是为什么我们这样写:

x not in used and (used.append(x) or True)

因此, 仅当表达式的第一部分(x not in used)True ,我们才能评估 used.append(x)并获得True的结果。

在第二种方法与reduce方法中可以看到类似的方式。

(l.append(x) or l) if x not in l else l
#similar as the above, but maybe more readable
#we return l unchanged when x is in l
#we append x to l and return l when x is not in l
l if x in l else (l.append(x) or l)

我们在哪里:

  1. x附加到l并在x不在l时返回l 。由于or语句,将评估.append并在之后返回l
  2. xl时返回l不变