2012-11-02 47 views
5

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

我真的很喜欢这个代码=) – katrielalex

+5

@katrielalex - 我不。使用条件去除集合中的项目和项目会导致混淆,难以阅读代码。 (恕我直言) – mgilson

+1

另外,你正在创建一个全新的'set',并弹出它的每一项,只是作为一个列表上的过滤器。我无法想象这会更快,而且它绝对不是一次性通过(用于创建新的清单)或双通(用于就地清单清单)'for for循环。 –

回答

9

有一个检查if item in uniq该项目被删除之前它被执行。 and运营商很好,因为它“短路”。这意味着如果左边的条件评估为False样,那么右边的条件就不会被评估 - 我们已经知道表达式不能像True那样。

+0

非常感谢。 uniq.remove(item)返回什么值?我猜测整个“而不是uniq.remove(item)”是一种在列表理解中运行方法而不是将整个东西改为for循环的方法,但我不确定为什么,例如,我们使用“而不是”在这种情况下,而不是“和”。“假定b/c unique.remove(item)返回None或False? – user1794459

+0

'uniq.remove(item)'返回'None'。 'not None'返回'True'。 – mgilson

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

分解它,使其变得简单:)并非所有事物都必须成为列表理解。

+1

当然,不是一切!因为我们有词典的理解和发电机的理解:) – Kos

+1

我们做!但有时候一个老式的循环就好了! –

+1

我没有问题,这是一个使列表唯一的方式 - 但我不认为这有助于OP的概念理解为什么表达式实际工作。 – mgilson

-1

第一次运行此功能时,您将从列表理解中获得[1,2,3,4],并且设置uniq将被清空。第二次运行此功能时,您将获得[],因为您的设置uniq将为空。你在第二次运行时没有得到任何错误的原因是Python的and短路 - 它看到第一个子句(item in uniq)是错误的,并且不打算运行第二个子句。

+0

我很抱歉downvote,但这只是不明确。你第二次运行你会得到'[]'的函数是什么意思?为什么set'uniq'是空的? – mgilson

+0

'uniq'为空,因为'uniq.remove(item)'清空它。列表理解不会通过第一次短路。我会编辑我的答案来拼出来。 – dshapiro

+0

'uniq'每次被''uniq = set(seq)'' – mgilson

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),或者两个条件都被评估或者两者都不是。

第二个条件存在的原因是从uniq中删除item。这样,如果/当您再次遇到item(作为seq中的副本),它将不会在uniq中找到,因为它最后一次在uniq中被删除。

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

希望这有助于

+0

“第二个条件的主要原因......” - >“仅**的第二个条件的原因......”:D。值得指出的是,有些人认为使用这种副作用的条件有点粗鲁。 – mgilson

+0

@mgilson:正式注意!回答更新:) – inspectorG4dget

+0

非常明确的回复,谢谢。 – user1794459

0

@ mgilson的答案是正确的,但在这里,你的信息,是一个可能的懒惰(generator)相同的功能的版本。这意味着它将适用于不适合内存的迭代器 - 包括无限迭代器 - 只要它的元素集合就可以。

def unique(iterable): 
    uniq = set() 
    for item in iterable: 
     if item not in uniq: 
      uniq.add(item) 
      yield item 
1

mgilson和其他人一如既往地很好地回答了这个问题。我想我可能会指出什么是可能在Python这样做,即使用从itertools文档的recipe sectionunique_everseen配方的规范方式,引用如下:

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 
相关问题