2012-11-02 71 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 
相關問題