从python中的列表中获取唯一值

2024-04-25 13:05:50 发布

您现在位置: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)

有没有更好的解决办法?


Tags: 代码in列表foroutputifnotjob
3条回答

如果我们需要保持元素的顺序,那么:

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变量的解决方案

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问了一个很好的问题“这是如何工作的?”。对于每个有问题的人来说。我将试着更深入地解释这是如何运作的,以及这里发生了什么巫术;)

所以她首先问:

I try to understand why unique = [used.append(x) for x in mylist if x not in used] is not working.

好吧,它确实起作用了

>>> 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

但如果我们太天真了,就跟着:

>>> 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. x位于used时,计算结果为False
  2. x不在used中时,计算结果为None

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

但是为什么当x不在used中时,它的计算结果是None?有人可能会问。

因为这就是Python的short-circuit操作符works的方式。

The expression x and y first evaluates x; if x is false, its value is returned; otherwise, y is evaluated and the resulting value is returned.

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

但这就是我们想要的,为了从一个有重复项的列表中获得唯一的元素,我们想要.append它们只有在我们第一次遇到它们时才进入一个新的列表。

所以我们真的想只在x不在used的情况下计算used.append(x),也许有办法把这个None值变成truthy值,我们就没事了,对吧?

是的,这里是第二种类型的short-circuit运算符开始使用的地方。

The expression x or y first evaluates x; if x is true, its value is returned; otherwise, y is evaluated and the resulting value is returned.

我们知道.append(x)永远是falsy,所以如果我们在他旁边加上一个or,我们就会得到下一个部分。所以我们写:

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

因此,只有当表达式的第一部分是^{(x not in used)时,我们才能对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未触及

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

mylist = list(set(mylist))

首先正确地声明你的列表,用逗号隔开。可以通过将列表转换为集合来获取唯一值。

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)

正如有人指出的那样,套数并不能维持原来的顺序。如果需要,应该寻找ordered set实现(有关更多信息,请参见this question)。

相关问题 更多 >