Python:如何检查项目是否已添加到集合中,而不使用2x(哈希、查找)
我想知道是否有一种清晰/简洁的方法可以向集合中添加某些内容,并检查它是否是在没有2x哈希和查找的情况下添加的 这是您可能要做的,但它有2个散列项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
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 (!=)