如果我们需要保持元素的顺序,如何:
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, [])
更新 - 2020 年 12 月 - 也许是最好的方法!
从 python 3.7 开始,标准dict保留插入顺序。
在 3.7 版更改: 字典顺序保证为插入顺序。这种行为是 CPython 3.6 的实现细节。
因此,这使我们能够使用dict.from_keys
重复数据删除!
注意:感谢@rlat在评论中为我们提供了这种方法!
mylist = [u'nowplaying', u'PBS', u'PBS', u'nowplaying', u'job', u'debate', u'thenandnow']
unique = list(dict.fromkeys(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, [])
注意:请记住,我们得到的可读性更高,脚本性能更差。除了dict.from_keys
特定于 python 3.7+ 的方法。
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.2029558869980974
timeit.timeit('[x for x in mylist if x not in used and (used.append(x) or True)]', setup='used = [];'+setup)
0.28999493700030143
# 10x to rlat for suggesting this approach!
timeit.timeit('list(dict.fromkeys(mylist))', setup=setup)
0.31227896199925453
timeit.timeit('reduce(lambda l, x: l.append(x) or l if x not in l else l, mylist, [])', setup='from functools import reduce;'+setup)
0.7149233570016804
timeit.timeit('reduce(lambda l, x: l+[x] if x not in l else l, mylist, [])', setup='from functools import reduce;'+setup)
0.7379565160008497
timeit.timeit('reduce(lambda l, x: l if x in l else l+[x], mylist, [])', setup='from functools import reduce;'+setup)
0.7400134069976048
timeit.timeit('[x for i, x in enumerate(mylist) if i == mylist.index(x)]', setup=setup)
0.9154880290006986
回答评论
因为@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
在左侧返回。
但是如果我们太天真了,那就去吧:
>>> 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
这基本上总是:
- 评估
False
何时x
在 中used
,
- 评估
None
何时x
不在 中used
。
在这两种情况下(False
/ None
),这将被视为falsy
值,结果我们将得到一个空列表。
但是为什么这会评估为None
when x
is not inused
呢?有人可能会问。
嗯,这是因为这就是 Python 的短路运算符的工作方式。
该表达式x and y
首先计算 x;如果 x 为假,则返回其值;否则,评估 y 并返回结果值。
因此,何时x
不使用(即当它的True
)下一部分或表达式将被评估(used.append(x)
)并返回其值(None
)。
但这就是我们想要从具有重复项的列表中获取唯一元素的目的,我们希望.append
仅当我们第一次遇到它们时才将它们放入一个新列表中。
所以我们真的只想used.append(x)
在x
不在的时候评估used
,也许如果有办法把这个None
值变成truthy
一个我们会没事的,对吧?
嗯,是的,这就是第二种类型的short-circuit
运营商发挥作用的地方。
该表达式x or y
首先计算 x;如果 x 为真,则返回其值;否则,评估 y 并返回结果值。
我们知道这.append(x)
将永远是falsy
,所以如果我们只是在or
他旁边添加一个,我们将永远得到下一部分。这就是我们写的原因:
x not in used and (used.append(x) or True)
所以我们可以评估 used.append(x)
并得到True
结果,只有当表达式的第一部分(x not in used)
是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)
我们在哪里:
- 追加并
x
返回when不在. 由于该语句被评估并在此之后返回。l
l
x
l
or
.append
l
- 进入
l
时原封不动地返回x
l