2013-10-27 41 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

相关问题