如何从集中检索一个元素而不删除它?

时间:2020-03-05 18:52:29  来源:igfitidea点击:

假设以下内容:

>>> s = set([1, 2, 3])

如何在不执行s.pop()的情况下从s中获得一个值(任何值)?我想将该项目保留在集合中,直到我确定可以将其删除为止,只有在异步调用另一台主机之后,我才能确定该项目。

快速而肮脏:

>>> elem = s.pop()
>>> s.add(elem)

但是我们知道更好的方法吗?理想情况下是恒定时间。

解决方案

回答

不需要复制整个集合的两个选项:

for e in s:
    break
# e is now an element from s

或者...

e = next(iter(s))

但是总的来说,集合不支持索引或者切片。

回答

另一种选择是将字典与不需要的值一起使用。例如。,

poor_man_set = {}
poor_man_set[1] = None
poor_man_set[2] = None
poor_man_set[3] = None
...

我们可以将键视为一个集合,但它们只是一个数组:

keys = poor_man_set.keys()
print "Some key = %s" % keys[0]

这种选择的副作用是代码将与旧的,预置的Python版本向后兼容。这可能不是最佳答案,但这是另一种选择。

编辑:我们甚至可以执行以下操作来隐藏使用dict而不是数组或者集合的事实:

poor_man_set = {}
poor_man_set[1] = None
poor_man_set[2] = None
poor_man_set[3] = None
poor_man_set = poor_man_set.keys()

回答

由于我们需要一个随机元素,因此也可以使用:

>>> import random
>>> s = set([1,2,3])
>>> random.sample(s, 1)
[2]

该文档似乎没有提到random.sample的性能。从包含大量列表和大量集合的非常快速的实证检验来看,列表似乎是恒定时间,但集合却并非如此。同样,对集合的迭代也不是随机的。顺序不确定,但可以预测:

>>> list(set(range(10))) == range(10)
True

如果随机性很重要,并且我们需要在恒定时间内(大集合)需要一堆元素,那么我将使用random.sample并首先转换为列表:

>>> lst = list(s) # once, O(len(s))?
...
>>> e = random.sample(lst, 1)[0] # constant time

回答

最少的代码是:

>>> s = set([1, 2, 3])
>>> list(s)[0]
1

显然,这将创建一个包含该集合的每个成员的新列表,因此如果集合很大,就不会很大。

回答

我使用编写的实用程序函数。它的名称有点误导,因为它暗示它可能是随机物品或者类似物品。

def anyitem(iterable):
    try:
        return iter(iterable).next()
    except StopIteration:
        return None

回答

为了提供不同方法背后的一些时序图,请考虑以下代码。
get()是我对Python的setobject.c的自定义添加,只是一个pop()而没有删除该元素。

from timeit import *

stats = ["for i in xrange(1000): iter(s).next()   ",
         "for i in xrange(1000): \n\tfor x in s: \n\t\tbreak",
         "for i in xrange(1000): s.add(s.pop())   ",
         "for i in xrange(1000): s.get()          "]

for stat in stats:
    t = Timer(stat, setup="s=set(range(100))")
    try:
        print "Time for %s:\t %f"%(stat, t.timeit(number=1000))
    except:
        t.print_exc()

输出为:

$ ./test_get.py
Time for for i in xrange(1000): iter(s).next()   :       0.433080
Time for for i in xrange(1000):
        for x in s:
                break:   0.148695
Time for for i in xrange(1000): s.add(s.pop())   :       0.317418
Time for for i in xrange(1000): s.get()          :       0.146673

这意味着for / break解决方案是最快的(有时比自定义get()解决方案更快)。