Python:如何检查项目是否已添加到集合中,而不使用2x(哈希、查找)

Python:如何检查项目是否已添加到集合中,而不使用2x(哈希、查找),python,python-3.x,set,Python,Python 3.x,Set,我想知道是否有一种清晰/简洁的方法可以向集合中添加某些内容,并检查它是否是在没有2x哈希和查找的情况下添加的 这是您可能要做的,但它有2个散列项 if item not in some_set: # <-- hash & lookup some_set.add(item) # <-- hash & lookup, to check the item already is in the set other_task() 使用Python的se

我想知道是否有一种清晰/简洁的方法可以向集合中添加某些内容,并检查它是否是在没有2x哈希和查找的情况下添加的

这是您可能要做的,但它有2个散列项

if item not in some_set:  # <-- hash & lookup
    some_set.add(item)    # <-- hash & lookup, to check the item already is in the set

    other_task()

使用Python的set api有更好的方法吗?

我认为没有一种内置的方法可以做到这一点。当然,您可以编写自己的函数:

def do_add(s, x):
  l = len(s)
  s.add(x)
  return len(s) != l

s = set()
print(do_add(s, 1))
print(do_add(s, 2))
print(do_add(s, 1))
print(do_add(s, 2))
print(do_add(s, 4))
或者,如果您更喜欢神秘的一行:

def do_add(s, x):
  return len(s) != (s.add(x) or len(s))
(这取决于从左到右的求值顺序以及
set.add()
始终返回
None
,这是错误的。)


除此之外,如果双哈希/查找明显是一个性能瓶颈,如果使用一个函数明显更快,那么我只会考虑这样做。

字典有很好的功能来避免与问题中提到的“双查找”有关的一类问题。因为,至少在CPython中,大多数集合代码都与字典共享,所以我在处理非常大的集合(500k+add,+/-10%重复条目)时尝试使用它

此外,为了减少Python符号名称查找所隐含的开销,我将其包装在一个高阶函数中,这样编译器将构建一个闭包,从而能够使用基于索引的/操作码,而不是更昂贵的基于名称查找的/:

在我的特定用例中,此解决方案比另一个答案中建议的解决方案运行速度快20%以上。当然,您的里程数可能会有所不同,因此您应该自己进行测试


以下是两种解决方案(在Linux上运行的Python3.5)的反汇编代码供参考:


@PadraicCunningham:问题是
other_task()
。也许您必须实现自己的set类。
add
方法已经为您执行了此测试,所以只需调用它。@PadraicCunningham:您没有理解OP的要点。如果元素不在集合中,OP想做一些事情——调用
other_task()
——这意味着他需要知道元素是否在集合中。他担心(在我看来,这是不必要的)重复查找对性能的影响,但简单地说“只添加它”是不相关的。@赛博,查找很快,但减少查找几乎肯定会更快,并且哈希函数不能保证对所有对象类型都很快(即使它们应该在理想情况下)。+=1,但神秘的oneliner必须是一个
lambda
,以获得额外的不可读性=(l.add(I)或l._len__;())
def do_add(s, x):
  return len(s) != (s.add(x) or len(s))
def cache():
  s = {}
  setdefault = s.setdefault
  n = 0

  def add(x):
    nonlocal n
    n+=1
    return setdefault(x,n) != n

  return add


# Usage
cached = cache()

for i in my_large_generator_with_duplicates():
  if not cached(i):
    do_something()
def do_add(s, x):
  l = len(s)
  s.add(x)
  return len(s) != l


dis.dis(do_add)

 20           0 LOAD_GLOBAL              0 (len)                                                                                                                                                                    
              3 LOAD_FAST                0 (s)                                                                                                                                                                      
              6 CALL_FUNCTION            1 (1 positional, 0 keyword pair)                                                                                                                                           
              9 STORE_FAST               2 (l)                                                                                                                                                                      

 21          12 LOAD_FAST                0 (s)                                                                                                                                                                      
             15 LOAD_ATTR                1 (add)                                                                                                                                                                    
             18 LOAD_FAST                1 (x)                                                                                                                                                                      
             21 CALL_FUNCTION            1 (1 positional, 0 keyword pair)                                                                                                                                           
             24 POP_TOP                                                                                                                                                                                             

 22          25 LOAD_GLOBAL              0 (len)                                                                                                                                                                    
             28 LOAD_FAST                0 (s)                                                                                                                                                                      
             31 CALL_FUNCTION            1 (1 positional, 0 keyword pair)
             34 LOAD_FAST                2 (l)
             37 COMPARE_OP               3 (!=)
def cache():
  s = {}
  setdefault = s.setdefault
  n = 0

  def add(x):
    nonlocal n
    n+=1
    return setdefault(x,n) != n

  return add


dis.dis(cache.__code__.co_consts[2])


 13           0 LOAD_DEREF               0 (n)
              3 LOAD_CONST               1 (1)
              6 INPLACE_ADD
              7 STORE_DEREF              0 (n)

 14          10 LOAD_DEREF               1 (setdefault)
             13 LOAD_FAST                0 (x)
             16 LOAD_DEREF               0 (n)
             19 CALL_FUNCTION            2 (2 positional, 0 keyword pair)
             22 LOAD_DEREF               0 (n)
             25 COMPARE_OP               3 (!=)