2013-10-27 71 views
2

了一組工作時,一個常見的模式是:是否有'dict.setdefault'等價於集合?

number_list = [1,5,7,2,4,4,1,3,8,5] 
number_set = set() 

for number in number_list: 

    #we only want to process the number if we haven't already processed it 
    if(number not in number_set): 
     number_set.add(number) 

     #do processing of 'number' here now that we know it's not a duplicate 

線條if(number not in number_set):number_set.add(number)來煩我,因爲我們在這裏做兩個哈希查找,當現實,我們應該只需要一個。

字典有「setdefault」操作,它解決了一個非常類似的問題:「如果鍵存在於字典中,則返回值,否則插入此默認值,然後返回默認值」。如果你這樣做天真,IE下面,執行兩個哈希查找,但setdefault讓你做一個

if item_key in dict: 
    dict[item_key].append(item_value) 
else: 
    dict[item_key] = [item_value] 

是否有套等效操作?像if(number_set.check_if_contains_and_then_add(number)):,但給了一個更好的名字。

+0

爲什麼你不能只是'number_set = set(number_list)'? –

+0

@AshishNitinPatil在我給出的例子中是正確的選擇,但是如果你在開始之前不知道列表的全部內容,或者如果你有一個你不想消費的iderator全部一起 – Elliott

+0

由於某些東西不能在一個集合中多次出現,只需無條件地添加它,如果它已經存在,則不會有任何變化,也不會造成任何損害。 – martineau

回答

2

如果分析器告訴你,哈希查找貢獻顯著運行,那麼這可能會解決它。

def add_value(container, value): 
    oldlen = len(container) 
    container.add(value) 
    return len(container) != oldlen 

if add_value(number_set, number): 
    # process number 

但是,爲什麼呢?也許是由於緩慢的方法,儘管現在我可以告訴你(a)散列整數不是很慢,並且(b)如果可能的話,最好使緩存的結果緩慢而不是減少結果通話次數。或者可能是由於緩慢的__eq__,這很難處理。最後,如果內部查找機制本身很慢,那麼爲了加快程序的運行速度可能不會很多,因爲運行時一直在執行哈希查找,在範圍內查找名稱。

set.add可能會很好地返回一個值,指示該集合是否更改,但我認爲這個想法違背了Python庫的原則(承認不是普遍支持),即變異操作不返回一個價值,除非它是該操作的基礎。所以pop()函數當然會返回一個值,但list.sort()返回None,即使它返回self時偶爾會對用戶有用。

我想你可以做這樣的事情:

def deduped(iterable): 
    seen = set() 
    count = 0 
    for value in iterable: 
     seen.add(value) 
     if count != len(seen): 
      count += 1 
      yield value 

for number in deduped(number_list): 
    # process number 

當然,這是純粹的猜測,反覆哈希查找是什麼樣的問題:我通常會寫其中的任意一種功能與if not in測試中您的原始代碼和函數的目的是簡化調用代碼,而不是避免多餘的哈希查找。

2

不,沒有。

setdefault方法用於設置字典中鍵的默認值,集合沒有值,因此完全沒有意義。

如果順序無關緊要,試試這個。

number_list = [1,5,7,2,4,4,1,3,8,5] 
number_set = set(number_list) 

for number in number_set: 
    #do processing of 'number' here now that we know it's not a duplicate 
+1

好的答案,但你的意思是失去對'add()'方法的無意義調用。 – Duncan

+0

@Duncan是的,我一開始並沒有注意到它。感謝您的提醒。 – OdraEncoded

+0

此答案適用於預先知道整個列表的情況,但如果您使用的是不想一次性使用的迭代器,或者對於像廣度優先搜索等整個列表未知的情況在算法 – Elliott

0

你爲什麼不做number_set.add(number)? setdefault的要點是它不會覆蓋鍵的現有值(如果存在)。但是一套沒有價值,只是一個關鍵,所以重寫是無關緊要的。

+1

「爲什麼你不只是'number_set.add(number)'?」 - 因爲如果沒有'如果不在''測試中,'number_list'中的任何重複項都會被處理兩次。 –

0

沒有有一個爲sets沒有setdefault類型的方法,但你可以做這樣的事情:

number_list = [1,5,7,2,4,4,1,3,8,5] 
number_set = set() 

for number in number_list: 
    if number not in number_set and not number_set.add(number): 
     #do somethihng here 

not number_set.add(number)條件將被稱爲只有number not in number_setTrue

使用此功能,您可以按照有序的方式處理獨特的物品(保留訂單)。

>>> number_list = [1,5,7,2,4,4,1,3,8,5] 
>>> seen = set() 
>>> [x for x in number_list if x not in seen and not seen.add(x)] 
[1, 5, 7, 2, 4, 3, 8] 

如果順序並不重要,那麼只需調用number_listset()

>>> set(number_list) 
{1, 2, 3, 4, 5, 7, 8} 
+0

我喜歡這樣做在一行中的兩個操作,但我覺得'而不是number_set.add(number)'將是不可讀的 - 的確,「void」方法實際上返回None,但很難區分它與依賴一個實際的布爾返回值 – Elliott

+0

我會寫'number_set.add(number)是None'而不是'not number_set.add(number)'。我似乎更清楚。 –

+1

把這個函數放在if子句的條件裏是絕對沒有意義的。 – OdraEncoded

相關問題