Как итерироваться по всем элементам множества (set) в Python?
Что проверяют
Вопрос простой, поэтому ждут не только for x in s, а понимания, что множество не упорядочено и его нельзя менять во время обхода. Следом часто спрашивают, почему порядок элементов «скачет» между запусками и как получить стабильный.
Как отвечать
- обычный обход:
for item in my_set:работает за O(n), порядок определяется хэшами и внутренней таблицей, а не порядком добавления. Порядок вставки гарантирован уdictначиная с Python 3.7, уsetтакой гарантии нет; - стабильный порядок:
for item in sorted(my_set):даёт предсказуемый результат за O(n log n). Если нужен порядок вставки, в роли упорядоченного множества используютdict.fromkeys(items); - строки и рандомизация: хэши
strиbytesсолятся при каждом запуске интерпретатора (PYTHONHASHSEED), поэтому порядок множества строк меняется от запуска к запуску. У маленьких целых чисел порядок часто выглядит отсортированным, но это совпадение, а не свойство; - индексы:
enumerate(my_set)сработает, но номер ничего не значит. Обратиться по индексу нельзя,my_set[0]даётTypeError; - изменение во время обхода: добавление или удаление внутри цикла приводит к
RuntimeError: Set changed size during iteration. Обходите копиюfor x in set(s):или собирайте новое множество включением{x for x in s if cond(x)}; frozenset: обходится так же, но неизменяем и хэшируем, поэтому годится в элементы другого множества и в ключи словаря.
Достать единственный элемент без цикла можно через next(iter(s)), а s.pop() вернёт произвольный элемент и удалит его.
Где спотыкаются
Утверждают, что множество хранит порядок добавления, потому что так совпало на {1, 2, 3}. Интервьюер тут же попросит повторить тот же пример со строками.
Удаляют элементы прямо в цикле по тому же множеству. Правильно обходить копию или строить новое множество, а для массового удаления есть s -= to_remove и difference_update().
Показывают генератор (x for x in s) как «ещё один способ итерации». Это тот же обход с лишней обёрткой. Сильнее прозвучит генератор по делу: ленивая фильтрация внутри sum() или any().