下面是一个简单的功能,可以在保留顺序的同时删除列表中的重复项。我已经尝试过了,它确实有效,所以这里的问题是我的理解。在我看来,第二次为特定项目运行uniq.remove(item)
时,它会返回错误(我认为KeyError
或ValueError
?)因为该项目已从独特的一套。这不是这种情况吗?
def unique(seq):
uniq = set(seq)
return [item for item in seq if item in uniq and not uniq.remove(item)]
答案 0 :(得分:9)
在删除项目之前会检查if item in uniq
。 and
运算符很好,因为它“短路”。这意味着如果左边的条件评估为False
- 就像,那么右边的条件不会被评估 - 我们已经知道表达式不能是True
- 就像。 / p>
答案 1 :(得分:4)
set.remove
是一个就地操作。这意味着它不返回任何内容(好吧,它返回None
); bool(None)
为False
。
所以你的列表理解实际上是这样的:
answer = []
for item in seq:
if item in uniq and not uniq.remove(item):
answer.append(item)
并且因为python会使条件短路(正如其他人指出的那样),这实际上是:
answer = []
for item in seq:
if item in uniq:
if not uniq.remove(item):
answer.append(item)
当然,由于unique.remove(item)
返回None
(bool
为False
),因此要么评估这两个条件,要么都不评估。
存在第二个条件的原因是从item
中删除uniq
。这样,如果/当您再次遇到item
时(在seq
中重复),则uniq
中找不到它,因为它最后一次从uniq
删除它被发现了。
现在,请记住,这是相当危险的,因为修改变量的条件被认为是错误的样式(想象一下当你不完全熟悉它的作用时调试这样的条件)。条件语不应该修改它们检查的变量。因此,他们应该只读取变量,而不是写入变量。
希望这有帮助
答案 2 :(得分:1)
mgilson和其他人像往常一样很好地回答了这个问题。我想我可能会指出在python中执行此操作的规范方法,即使用unique_everseen
文档的recipe section中的itertools
配方,引用如下:
from itertools import ifilterfalse
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 ifilterfalse(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
答案 3 :(得分:0)
def unique_with_order(seq):
final = []
for item in seq:
if item not in final:
final.append(item)
return final
print unique_with_order([1,2,3,3,4,3,6])
将其分解,简化:)现在并非一切都必须是列表理解。
答案 4 :(得分:0)
@ mgilson的答案是正确的,但在这里,对于您的信息,是一个可能的懒惰(generator)版本的相同功能。这意味着它将适用于不适合内存的迭代 - 包括无限迭代器 - 只要它的元素集合就可以。
def unique(iterable):
uniq = set()
for item in iterable:
if item not in uniq:
uniq.add(item)
yield item
答案 5 :(得分:-1)
首次运行此功能时,您将从列表推导中获得[1,2,3,4]
,并清空集uniq
。第二次运行此功能时,您将获得[]
,因为您的设置uniq
将为空。你在第二次运行中没有得到任何错误的原因是Python的and
短路 - 它看到第一个子句(item in uniq
)是假的,并且不打算运行第二个子句。 / p>