我认为这应该引发错误,但事实并非如此

时间:2012-11-02 14:12:42

标签: python error-handling

下面是一个简单的功能,可以在保留顺序的同时删除列表中的重复项。我已经尝试过了,它确实有效,所以这里的问题是我的理解。在我看来,第二次为特定项目运行uniq.remove(item)时,它会返回错误(我认为KeyErrorValueError?)因为该项目已从独特的一套。这不是这种情况吗?

def unique(seq):
    uniq = set(seq)  
    return [item for item in seq if item in uniq and not uniq.remove(item)]

6 个答案:

答案 0 :(得分:9)

在删除项目之前会检查if item in uniqand运算符很好,因为它“短路”。这意味着如果左边的条件评估为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)返回NoneboolFalse),因此要么评估这两个条件,要么都不评估。

存在第二个条件的原因是从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>