如何从集合中获取元素而不移除它?

679 投票
15 回答
894820 浏览
提问于 2025-04-11 09:15

假设有以下情况:

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

我想从 s 中获取一个值(任何值),但不想使用 s.pop()。我希望这个值能留在集合里,直到我确认可以把它移除——而我只有在异步调用另一个主机后才能确认。

简单粗暴的方法:

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

不过,你知道有没有更好的方法吗?理想情况下是常量时间的。

15 个回答

207

我想知道这些函数在不同数据集上的表现,所以我做了个性能测试:

from random import sample

def ForLoop(s):
    for e in s:
        break
    return e

def IterNext(s):
    return next(iter(s))

def ListIndex(s):
    return list(s)[0]

def PopAdd(s):
    e = s.pop()
    s.add(e)
    return e

def RandomSample(s):
    return sample(s, 1)

def SetUnpacking(s):
    e, *_ = s
    return e

from simple_benchmark import benchmark

b = benchmark([ForLoop, IterNext, ListIndex, PopAdd, RandomSample, SetUnpacking],
              {2**i: set(range(2**i)) for i in range(1, 20)},
              argument_name='set size',
              function_aliases={first: 'First'})

b.plot()

这里插入图片描述

这个图清楚地显示出,有些方法(比如 RandomSampleSetUnpackingListIndex)的表现会受到数据集大小的影响,因此在一般情况下应该避免使用这些方法(至少如果你在意性能的话)。正如其他回答所提到的,最快的方法是 ForLoop

不过,只要使用了某些常量时间的方法,性能差异就几乎可以忽略不计。


iteration_utilities(免责声明:我是这个库的作者)里面有一个方便的函数可以用在这种情况下:first

>>> from iteration_utilities import first
>>> first({1,2,3,4})
1

我也把它包含在上面的性能测试中。它可以和另外两个“快速”的解决方案相媲美,但无论如何,差别都不大。

213

最简单的代码如下:

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

显然,这段代码会创建一个新列表,里面包含了集合中的每一个成员。如果你的集合很大,这样做就不太好了。

837

有两种方法可以做到这一点,而不需要复制整个集合:

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

或者...

e = next(iter(s))

不过一般来说,集合是不支持索引或切片的。

撰写回答