in: проверка ключа в словаре
Оператор in отвечает на вопрос «есть ли такой ключ»: проверка перед доступом вместо KeyError, not in, грабли с проверкой значений и причина скорости поиска.
Редакция Питоники
К этому моменту у нас два способа чтения: строгие квадратные скобки, которые падают с KeyError, и get с умолчанием из урока 4. Остался третий, самый прямолинезный вопрос: «а есть ли ключ вообще?». На него отвечает оператор in — последний кирпич в фундаменте работы со словарями. Оператор in отвечает на один вопрос: есть ли такой ключ в словаре, — и честно возвращает True или False.
Оператор in: есть ли ключ
Слева — ключ, слово in, справа — словарь. Результат — булево значение True или False, готовое для печати и для условий:
stock = {'молоко': 12, 'хлеб': 5}
print('молоко' in stock)
print('кефир' in stock)
True False
stock = {'молоко': 12, 'хлеб': 5}
print(12 in stock)
print(12 in stock.values())
print('кефир' not in stock)
False True True
Проверка перед доступом
Самая частая роль in — страховка перед чтением по ключу. Сначала спрашиваем, потом достаём: если ключа нет, программа не упадёт с KeyError, а пойдёт по мирной ветке:
stock = {'молоко': 12, 'хлеб': 5}
key = 'кефир'
if key in stock:
print('есть:', stock[key])
else:
print('нет:', key)
нет: кефир
Это лекарство от ошибки из урока про KeyError: доступ stock[key] внутри условия выполняется только при наличии ключа. Тот же результат даёт get с умолчанием — выбор между ними по вкусу: in хорош, когда по наличию ключа нужно принять решение с двумя разными действиями, get — когда хватит запасного значения.
| Задача | Запись | Ключа нет — что будет |
|---|---|---|
| Прочитать строго | stock[key] | KeyError — программа падает |
| Прочитать мягко | stock.get(key, 0) | вернётся 0 |
| Сначала проверить | key in stock | False — и решай сам |
Три представления словаря
Раз уж оператор in умеет проверять и ключи, и значения, стоит разобраться, откуда берётся эта разница. У словаря три представления: keys() отдаёт ключи, values() — значения, items() — пары целиком. И in смотрит ровно на то представление, к которому обратились: in d равносильно in d.keys(), а вот in d.values() — это уже другая проверка, перебирающая значения.
stock = {'молоко': 12, 'хлеб': 5}
names = list(stock.keys())
values = list(stock.values())
pairs = list(stock.items())
print(names)
print(values)
print(pairs)
['молоко', 'хлеб']
[12, 5]
[('молоко', 12), ('хлеб', 5)]Три списка из одних и тех же двух пар. Именно поэтому in по словарю проверяет ключи: это ближайший по смыслу представление, а значения — вторичны. Список значений приходится строить явно через list(...), потому что само представление stock.values() — это не список, а объект-обзор, который обновляется вместе со словарём.
stock = {'молоко': 12, 'хлеб': 5}
print('молоко' in stock)
print('Молоко' in stock)
print('молоко' in stock.keys())
print('12' in stock)
True False True False
Четыре проверки одного словаря. Вторая и третья дают разный ответ из-за регистра: Молоко с заглавной — другой ключ, и словарь его не находит. Четвёртая ищет строку '12' среди ключей и не находит, потому что 12 — это число, а не строка. Проверка значений выглядит иначе: 12 in stock.values() вернёт True.
Сценарий: разбор заказа
Соберём типичную задачу, где in — не украшение, а необходимость. Есть словарь заказов: код заказа — статус. Часть заказов в пути, часть доставлены, часть вовсе не в системе. Нужно получить отчёт по тем, что ещё едут, и отдельно посчитать, сколько кодов вообще нет в системе.
orders = {'A-1': 'доставлен', 'B-2': 'в пути'}
for code, status in orders.items():
if status == 'в пути':
print('ждём:', code)
ждём: B-2
Здесь in не понадобился — условие по значению. А вот для второй части задачи — «каких кодов нет в системе» — без in не обойтись: коды приходят снаружи, и обращаться к ним напрямую опасно, KeyError уронит программу на первом же неизвестном коде.
orders = {'A-1': 'доставлен', 'B-2': 'в пути'}
incoming = ['A-1', 'C-9', 'B-2', 'D-4']
unknown = []
for code in incoming:
if code not in orders:
unknown.append(code)
print('неизвестные коды:', unknown)
неизвестные коды: ['C-9', 'D-4']
Важно: not in использован как отдельная конструкция, а не как not code in orders — так код читается быстрее. И ещё деталь: проверка идёт по ключам, а коды заказов и есть ключи, так что смысл совпадает. Если бы статус был ключом, а код — значением, пришлось бы переворачивать проверку.
Что дальше
Проверять наличие ключа вы умеете. Дальше — обход всего словаря целиком: цикл for по ключам, распаковка пар через items() и подсчёт суммы по значениям. В уроке 8 разбираем три представления словаря и грабли с изменением словаря во время перебора. А если хочется сгруппировать данные по ключу одним выражением, — урок 10 про setdefault.
Сколько стоит проверка
Раз уж речь о скорости, стоит проговорить конкретику. Проверка in по ключу — это вычисление хеша и один переход по таблице: время работы не зависит от того, три в словаре пары или три миллиона. На наборе из 100 000 пар разница между проверкой ключа и перебором списка из тех же 100 000 элементов составляет не единицы процентов, а разы: перебор линеен и на 100 000 элементах делает до 100 000 сравнений, хеш-таблица — одно.
Но есть важная оговорка: та же самая задача, решённая через in по значениям, снова становится линейной. 12 in stock.values() просмотрит все значения до конца. Если в словаре 100 000 пар и вы ищете значение, а не ключ, — это уже перебор. Вывод простой: проектируйте словарь так, чтобы искать по ключу. Тогда и проверка, и доступ, и обновление остаются быстрыми. Если же структура по природе своей список — берите список и не изобретайте словарь ради скорости, которой всё равно не будет.
stock = {'молоко': 12, 'хлеб': 5, 'сыр': 8}
for name in stock:
if name in stock:
print(name, stock[name])
молоко 12 хлеб 5 сыр 8
Этот код работает, но проверка name in stock здесь лишняя: мы и так идём по ключам, и каждый ключ заведомо есть. Такого рода «страховка от самого себя» — обычное дело в чужих программах, и она стоит лишнего обращения к таблице на каждом шаге. Код становится понятнее, когда проверка стоит там, где действительно может быть промах: на данных извне.
Сначала предскажи ответ в голове — это главный навык программиста.
stock = {'чай': 3, 'кофе': 1}
print('чай' in stock)
print('молоко' not in stock)
d = {'x': 10}
print(10 in d)
print(10 in d.values())
stock = {'молоко': 12}
key = 'молоко'
if key in stock:
print(stock[key] + 3)
else:
print(0)
1. Что вернёт выражение 'молоко' in stock?
2. Словарь stock = {'молоко': 12}. Что вернёт 12 in stock?
3. Что означает запись 'кефир' not in stock?
4. Зачем перед доступом пишут if key in stock?
5. Почему проверка key in stock быстрая даже на огромном словаре?
6. Чем in отличается от in d.keys()?
7. Что вернёт not stock.get('кефир'), если в словаре есть ключ 'кефир' со значением 0?
8. Как проверить, что код заказа отсутствует в словаре заказов, не уронив программу?
Дан словарь stock = {'молоко': 12}. Выведи результат проверки, есть ли в словаре кефир. Затем выведи количество молока, но только если ключ 'молоко' есть, — через if с in и запись вида 'молоко: 12'.
Как проверить, есть ли ключ в словаре Python?
Оператором in: 'молоко' in stock вернёт True, если ключ есть, и False, если нет. Для обратной проверки есть not in. Проверка булева и никогда не падает — в отличие от доступа по ключу.
Урок 3: что такое KeyErrorin проверяет ключи или значения?
Только ключи: запись 12 in stock смотрит ключи словаря. Проверить значения можно отдельно: 12 in stock.values(). Это важно помнить, потому что False на первый взгляд «есть в словаре» значение — частый сюрприз у начинающих.
Что лучше: проверка через in или get с умолчанием?
Оба способа страхуют от KeyError, выбор зависит от задачи. Если по наличию ключа нужны два разных действия — берите if key in stock. Если достаточно запасного значения для подсчёта — короче stock.get(key, 0). Часто в одном коде уместны оба.
Урок 4: метод getНасколько быстра проверка ключа в большом словаре?
Практически мгновенна при любом размере: словарь — хеш-таблица, Python по ключу сразу вычисляет адрес и не перебирает пары. Проверка значений через values() — другой случай: там значения просматриваются по одному.
Что проверяет оператор in применительно к словарю?
Только ключи. 'молоко' in stock равносильно 'молоко' in stock.keys(). Чтобы проверить наличие значения, нужно написать иначе: 12 in stock.values() — такая проверка перебирает значения и на больших словарях заметно медленнее.
Как проверить, что ключ отсутствует, не вызывая KeyError?
Оператором not in: 'кефир' not in stock вернёт True, если ключа нет. Он ничего не читает по ключу и никогда не падает. Альтернатива — get с умолчанием, но для вопроса о наличии ключа not in написан прямо и читается однозначно.
Понравился урок? Сошлитесь на него
«Оператор in отвечает на один вопрос: есть ли такой ключ в словаре, — и честно возвращает True или False.»
Скопируйте готовую ссылку в формате HTML, Markdown или чистый адрес и вставьте в статью на Habr, VC, Telegram-канал или свой блог — так о проекте узнают новые читатели.
Что дальше
Осталось в разделе: 20 из 20. Прогресс хранится в вашем браузере.
Что читать дальше
Словари · Урок 6
Удаление: del, pop и popitem
Четыре способа убрать данные из словаря: del молча удаляет пару, pop отдаёт значение, popitem снимает последнюю пару, clear чистит всё. Плюс разбор, почему удалять во время обхода нельзя.
Словари · Урок 4
get: безопасное чтение из словаря
Метод get читает словарь без KeyError: без ключа вернёт None, со вторым аргументом — запасное значение. Сравниваем с квадратными скобками, разбираем арифметику с None и молчание get.
Словари · Урок 3
Доступ по ключу и KeyError
Что происходит при доступе по несуществующему ключу: настоящий traceback, построчный разбор KeyError, отлов через try/except и случаи, когда падение — правильное поведение.
Похожие уроки по темам
Подобраны автоматически по пересечению тем и ключевых слов.
Исключения · Урок 11
KeyError в Python: ошибки словарей
Словарь не нашёл ключ — и честно сказал об этим KeyError. Разбираем объект ошибки, читаем из него ключ и выбираем между get, in и честным исключением.
keyerror словарь pythonpython словарь нет ключа
Множества · Урок 5
in в Python: мгновенная проверка принадлежности множеству
Один оператор, два механизма: in у множества отвечает мгновенно, у списка — перебором. Почему на миллионе элементов разница становится днями и секундами.
python проверка принадлежностиpython скорость поиска
Словари · Урок 5
Добавление и изменение пар в словаре
Одна операция на всё: d[ключ] = значение добавляет новую пару или заменяет старую. Плюс уникальность ключей, порядок вставки и счётчики, которые пишутся в одну строку.
python словарь добавитьдобавить элемент в словарь python