7

Как получить элемент из множества без его удаления?

5

Вопрос о получении значения из множества в Python без удаления элемента

У меня есть следующая ситуация:

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

Как я могу получить значение (любое значение) из множества s, не используя метод s.pop()? Я хочу оставить элемент в множестве до тех пор, пока не смогу его удалить – что я смогу сделать только после асинхронного вызова к другому хосту.

Вот временное решение:

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

Но не можете ли вы предложить более эффективный способ? Я бы хотел, чтобы это выполнялось за константное время.

5 ответ(ов)

8

Два способа, которые не требуют копирования всего множества:

for e in s:
    break
# e теперь является элементом из s

Или...

e = next(iter(s))

Однако в общем случае множества не поддерживают индексацию или срезы.

2

Наименьший код будет таким:

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

Этот подход использует итератор, что позволяет избежать создания нового списка с элементами множества. Это будет более эффективно, особенно если ваше множество очень большое.

0

В ответ на ваш вопрос о сравнении различных подходов к итерации по множеству, я провел некоторые замеры времени для различных подходов и вот что у меня получилось.

Код, который я использовал, выглядит следующим образом:

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().

0

Если вам нужен случайный элемент, можно использовать следующий код:

>>> 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)  # один раз, O(len(s))?
...
>>> e = random.sample(lst, 1)[0]  # постоянное время
0

Еще один способ в Python 3:

next(iter(s))

или

s.__iter__().__next__()

Оба варианта позволяют получить следующий элемент итератора s. Первый способ более распространён и читаем, поэтому его обычно предпочтительнее использовать.

Чтобы ответить на вопрос, пожалуйста, войдите или зарегистрируйтесь