Предположим следующее:
>>> s = set([1, 2, 3])
Как я могу получить значение (любое значение) s
без дела s.pop()
? Я хочу оставить элемент в наборе, пока не буду уверен, что смогу удалить его - в этом я могу быть уверен только после асинхронного вызова другого хоста.
Быстро и грязно:
>>> elem = s.pop()
>>> s.add(elem)
Но знаете ли вы лучший способ? Идеально в постоянное время.
union
т.д., не беря из него элементы. Например,next(iter({3,2,1}))
всегда возвращает,1
поэтому, если вы думали, что это вернет случайный элемент - это не так. Так, может быть, вы просто используете неправильную структуру данных? Какой вариант использования?Ответы:
Два варианта, которые не требуют копирования всего набора:
Или...
Но в целом наборы не поддерживают индексацию или нарезку.
источник
iter(s).next()
не брутто, но отлично. Полностью общий, чтобы взять произвольный элемент из любого итерируемого объекта. Ваш выбор, если вы хотите быть осторожным, если коллекция пуста.next(iter(your_list or []), None)
обрабатывать None множеств и пустых множествНаименее код будет:
Очевидно, это создаст новый список, который содержит каждого члена набора, так что не очень хорошо, если ваш набор очень большой.
источник
next(iter(s))
превышает толькоlist(s)[0]
на три символа и в остальном значительно превосходит как по времени, так и по сложности пространства. Таким образом, хотя утверждение «наименьшего кода» тривиально верно, также тривиально верно, что это наихудший возможный подход. Даже удаление вручную и затем повторное добавление удаленного элемента в исходный набор лучше, чем «создать целый новый контейнер только для извлечения первого элемента», что явно безумно. Больше всего меня беспокоит то, что 38 Stackoverflowers фактически проголосовали за это. Я просто знаю, что увижу это в рабочем коде.min(s)
используйте еще меньше символов, будучи столь же ужасным и неэффективным, как этот.min(s)
немного быстрее, чемnext(iter(s))
для наборов размера 1, и я пришел к этому ответу, специально ища особый случай извлечения единственного элемента из наборов размером 1.Мне было интересно, как функции будут работать для разных наборов, поэтому я сделал тест:
Этот сюжет ясно показывает , что некоторые подходы (
RandomSample
,SetUnpacking
иListIndex
) зависит от размера набора и его следует избегать в общем случае (по крайней мере , если производительность может быть важна). Как уже показали другие ответы, самый быстрый способForLoop
.Однако до тех пор, пока используется один из подходов с постоянным временем, разница в производительности будет незначительной.
iteration_utilities
(Отказ от ответственности: я автор) содержит удобную функцию для этого варианта использованияfirst
::Я также включил его в тест выше. Он может конкурировать с двумя другими «быстрыми» решениями, но в любом случае разница невелика.
источник
ТЛ; др
for first_item in muh_set: break
остается оптимальный подход в Python 3.x. Проклинаю тебя, Гвидо.ты делаешь это
Добро пожаловать в еще один набор времени Python 3.x, экстраполированный из wr. «S отлично Python 2.x-специфический ответ . В отличие от не менее полезного ответа AChampion на Python 3.x , приведенные ниже временные интервалы также предлагают решения, превышающие время, предложенные выше, включая:
list(s)[0]
, Новое решение Джона на основе последовательностей .random.sample(s, 1)
, дф. Эклектичное решение на основе RNG .Фрагменты кода для большой радости
Включите, настройте время:
Быстро устаревшие вневременные сроки
Вот! Упорядочено по самым быстрым и самым медленным фрагментам:
Faceplants для всей семьи
Неудивительно, что итерация вручную остается как минимум вдвое быстрее, чем следующее быстрое решение. Несмотря на то, что разрыв сократился по сравнению с плохим старым Python 2.x днями (в которых ручная итерация была как минимум в четыре раза быстрее), меня разочаровывает фанат PEP 20 , что самое подробное решение - лучшее. По крайней мере, преобразование набора в список просто для извлечения первого элемента набора так же ужасно, как и ожидалось. Спасибо Гвидо, пусть его свет будет продолжать направлять нас.
Удивительно, но решение на основе ГСЧ абсолютно ужасно. Преобразование списка плохое, но на
random
самом деле это просто ужасный соус. Так много для Бога случайных чисел .Я просто желаю аморфным Они бы уже нашли
set.get_first()
способ для нас. Если вы читаете это, они: «Пожалуйста. Сделайте что-нибудь».источник
next(iter(s))
это в два раза медленнее, чемfor x in s: break
в,CPython
это немного странно. Я имею в виду, что это такCPython
. Это будет примерно в 50-100 раз (или что-то в этом роде) медленнее, чем C или Haskell, выполняющие одно и то же (в большинстве случаев, особенно в итерации, без исключения хвостовых вызовов и без оптимизаций). Потеря некоторых микросекунд не имеет большого значения. Ты не думаешь? И есть также PyPyЧтобы предоставить некоторые временные показатели за различными подходами, рассмотрим следующий код. Get () - это мое собственное дополнение к setobject.c в Python, представляющее собой просто pop () без удаления элемента.
Выход:
Это означает, что решение for / break является самым быстрым (иногда быстрее, чем пользовательское решение get ()).
источник
for x in s
? «Итератор создан для результатаexpression_list
.»s.remove()
в миксiter
примерыfor
иiter
идут катастрофически плохо.Так как вы хотите случайный элемент, это также будет работать:
В документации, похоже, не упоминается производительность
random.sample
. Из действительно быстрого эмпирического теста с огромным списком и огромным набором, кажется, что постоянное время для списка, но не для набора. Кроме того, итерация по набору не случайна; порядок не определен, но предсказуем:Если случайность важна и вам нужно несколько элементов в постоянном времени (большие наборы), я бы
random.sample
сначала использовал и преобразовал в список:источник
choice()
, потому что Python попытается проиндексировать ваш набор, и это не работает.Казалось бы, самый компактный (6 символов), хотя и очень медленный способ получить элемент set (это стало возможным благодаря PEP 3132 ):
В Python 3.5+ вы также можете использовать это 7-символьное выражение (благодаря PEP 448 ):
На моей машине оба варианта примерно в 1000 раз медленнее, чем метод for-loop.
источник
Я использую функцию полезности, которую я написал. Его название несколько вводит в заблуждение, поскольку подразумевает, что это может быть случайный предмет или что-то в этом роде.
источник
После @wr. пост, я получаю аналогичные результаты (для Python3.5)
Вывод:
Тем не менее, при изменении базового набора (например, call to
remove()
) дела идут плохо для повторяемых примеров (for
,iter
):Результаты в:
источник
Что я обычно делаю для небольших коллекций, так это для создания такого метода синтаксического анализа / конвертера, как этот
Тогда я могу использовать новый список и доступ по номеру индекса
В качестве списка у вас будут все остальные методы, с которыми вам может потребоваться работать
источник
list
метод создания конвертера?Как насчет
s.copy().pop()
? Я не рассчитал это, но это должно работать, и это просто. Однако лучше всего подходит для небольших наборов, поскольку копирует весь набор.источник
Другой вариант - использовать словарь со значениями, которые вам не нужны. Например,
Вы можете рассматривать ключи как набор, за исключением того, что они являются просто массивом:
Побочным эффектом этого выбора является то, что ваш код будет обратно совместим с более
set
ранними версиями Python. Возможно, это не лучший ответ, но это другой вариант.Изменить: Вы можете даже сделать что-то вроде этого, чтобы скрыть тот факт, что вы использовали dict вместо массива или набора:
источник