5

下面是一个简单的函数,可以在保留顺序的同时删除列表中的重复项。我已经尝试过了,它确实有效,所以这里的问题是我的理解。在我看来,第二次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)]
4

6 回答 6

9

if item in uniq在删除项目之前执行检查。操作员很好,因为它“and短路”。这意味着如果左边的条件计算为False-like,那么右边的条件不会被计算——我们已经知道表达式不能是True-like。

于 2012-11-02T14:14:33.507 回答
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)返回Nonebool其中是False),要么评估两个条件,要么都不评估。

存在第二个条件的原因是从 中item删除uniq。这样,如果/当您item再次遇到(作为 中的重复项seq)时,将不会在其中找到它,因为它已从上次在那里找到时uniq被删除。uniq

现在,请记住,这是相当危险的,因为修改变量的条件被认为是不好的风格(想象一下当你不完全熟悉它的作用时调试这样的条件)。条件真的不应该修改他们检查的变量。因此,他们应该只读取变量,而不是写入它们。

希望这可以帮助

于 2012-11-02T14:29:05.370 回答
1

像往常一样,mgilson 和其他人很好地回答了这个问题。我想我可能会指出在 python 中执行此操作的规范方法可能是什么,即使用文档unique_everseen配方部分中的配方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
于 2012-11-02T14:43:33.090 回答
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])

把它分解,让它变得简单:) 这些天,并不是所有的东西都必须是一个列表理解。

于 2012-11-02T14:15:45.913 回答
0

@mgilson 的答案是正确的,但在这里,供您参考,是同一功能的可能的惰性(生成器)版本。这意味着它适用于不适合内存的迭代器——包括无限迭代器——只要它的元素集可以。

def unique(iterable):
    uniq = set()
    for item in iterable:
        if item not in uniq:
            uniq.add(item)
            yield item
于 2012-11-02T14:35:41.090 回答
-1

第一次运行这个函数时,你会[1,2,3,4]从你的列表理解中得到,并且集合uniq会被清空。第二次运行这个函数,你会得到[],因为你的集合uniq是空的。您在第二次运行时没有收到任何错误的原因是 Python 的and短路 - 它看到第一个子句 ( item in uniq) 是错误的,并且不会费心运行第二个子句。

于 2012-11-02T14:21:18.993 回答