Ответ 1
Во-первых, key in d.keys()
гарантированно даст вам то же значение, что и key in d
для любого dict d
.
И операция in
на объекте dict
или dict_keys
, который вы возвращаете из вызова keys()
на нем (в 3.x), не является O (N), это O (1).
Там нет реальной "оптимизации"; это просто, что использование хэша является очевидным способом реализации __contains__
в хеш-таблице, так же как это очевидный способ реализовать __getitem__
.
Вы можете спросить, где это гарантировано.
Ну, это не так. Типы сопоставления определяет dict
как, в основном, реализацию хеш-таблицы collections.abc.Mapping
. Ничто не мешает кому-либо создавать хеш-таблицу реализации Mapping, но все же обеспечивает поиск O (N). Но это была бы дополнительная работа, чтобы сделать такую плохую реализацию, так почему они?
Если вам действительно нужно это доказать самому себе, вы можете протестировать каждую свою реализацию (с профилировщиком или с помощью какого-то типа с пользовательскими __hash__
и __eq__
, которые регистрируют вызовы или...), или прочитайте источник.
В 2.x вы не хотите вызывать keys
, потому что он генерирует list
ключей, а не KeysView
. Вы можете использовать iterkeys
, но это может генерировать итератор или что-то еще, что не O (1). Поэтому просто используйте сам dict как последовательность.
Даже в 3.x вы не хотите вызывать keys
, потому что нет необходимости. Итерируя a dict
, проверяя его __contains__
и, вообще говоря, рассматривая его как последовательность, всегда эквивалентно тому, чтобы делать то же самое с его ключами, так зачем беспокоиться? (И, конечно, построение тривиального KeyView
и доступ через него собираются добавить несколько наносекунд к вашему времени работы и несколько нажатий клавиш к вашей программе.)
(Не совсем ясно, что использование операций последовательности эквивалентно для d.keys()
/d.iterkeys()
и d
в 2.x. Помимо проблем с производительностью, они эквивалентны в каждой реализации CPython, Jython, IronPython и PyPy, но он, кажется, нигде не указывается, как это делается в 3.x. И это не имеет значения, просто используйте key in d
.)
Пока мы на нем, обратите внимание, что это:
if(dict[key] != None):
... не будет работать. Если key
не находится в dict
, это приведет к повышению KeyError
, а не к возврату None
.
Кроме того, вы не должны проверять None
на ==
или !=
; всегда используйте is
.
Вы можете сделать это с помощью try
-или, проще говоря, сделать if dict.get(key, None) is not None
. Но опять же, нет причин для этого. Кроме того, это не будет обрабатывать случаи, когда None
является вполне допустимым элементом. Если это так, вам нужно сделать что-то вроде sentinel = object(); if dict.get(key, sentinel) is not sentinel:
.
Итак, правильно писать:
if key in d:
В более общем плане это неверно:
Я знаю, что ключевое слово "in", как правило, O (n) (поскольку это просто переводит на python, итерацию по всему списку и сравнение каждого элемента
Оператор in
, как и большинство других операторов, является просто вызовом метода __contains__
(или эквивалентом для встроенного C/Java/.NET/RPython). list
реализует его, итерируя список и сравнивая каждый элемент; dict
реализует его путем хэширования значения и поиска хэша; blist.blist
реализует его, прогуливаясь по дереву B +; и т.д. Таким образом, это могут быть O (n), O (1), O (log n) или что-то совершенно другое.