Проверьте, существует ли данный ключ в словаре


2683

Я хотел проверить, существует ли ключ в словаре, прежде чем обновлять значение ключа. Я написал следующий код:

if 'key1' in dict.keys():
  print "blah"
else:
  print "boo"

Я думаю, что это не лучший способ выполнить эту задачу. Есть ли лучший способ проверить ключ в словаре?


31
Вызов dict.keys()создает список ключей, согласно документации docs.python.org/2/library/stdtypes.html#dict.keys, но я был бы удивлен, если бы этот шаблон не был оптимизирован для, в серьезной реализации, для перевода к if 'key1' in dict:.
Евгений Сергеев

7
Так что я , наконец , понял, почему многие из моих сценариев Python так медленно :) :(. Это потому , что я использую x in dict.keys()для проверки ключей. И это произошло потому , что обычный способ для перебора ключей в Яве for (Type k : dict.keySet()), эта привычка вызывает for k in dict.keys()к чувствовать себя более естественно, чем for k in dict(что все еще должно быть хорошо с точки зрения производительности?), но затем проверка ключей становится if k in dict.keys()тоже, что является проблемой ...
Евгений Сергеев

4
@EvgeniSergeev if k in dict_:проверяет наличие k в KEYS dict_, поэтому вам все еще не нужно dict_.keys(). (Это меня немного поразило, так как читается как тестирование значения в dict. Но это не так.)
ToolmakerSteve

1
@ToolmakerSteve Это верно, но не только вам это не нужно, это не очень хорошая практика.
Евгений Сергеев

26
Попробуйте "ключ в dict"
marcelosalloum

Ответы:


3379

inэто предполагаемый способ проверки на наличие ключа в dict.

d = {"key1": 10, "key2": 23}

if "key1" in d:
    print("this will execute")

if "nonexistent key" in d:
    print("this will not")

Если вы хотели по умолчанию, вы всегда можете использовать dict.get():

d = dict()

for i in range(100):
    key = i % 10
    d[key] = d.get(key, 0) + 1

и если вы хотите всегда гарантировать значение по умолчанию для любого ключа, который вы можете использовать dict.setdefault()повторно или defaultdictиз collectionsмодуля, например, так:

from collections import defaultdict

d = defaultdict(int)

for i in range(100):
    d[i % 10] += 1

но в целом inключевое слово - лучший способ сделать это.


74
Я обычно просто использую, getесли я все равно собираюсь вытянуть предмет из словаря. Нет смысла использовать in и вытаскивать предмет из словаря.
Джейсон Бейкер

75
Я полностью согласен. Но если вам нужно только знать, существует ли ключ, или вам нужно различать случай, когда ключ определен, и случай, когда вы используете значение по умолчанию, inэто лучший способ сделать это.
Крис Б.

5
Ссылка на этот ответ находится в документации по
питону

30
get - плохой тест, если ключ эквивалентен «False», как, 0например, Узнал это трудным путем: /
Себастьян

4
Я не могу согласиться с тем, что это полный ответ, так как в нем не упоминается, что «попробуй» - «кроме» будет самым быстрым, когда число неудачных попыток ключа достаточно мало. Смотрите этот ответ ниже: stackoverflow.com/a/1602945/4376643
Крейг Хикс,

1547

Вам не нужно называть ключи:

if 'key1' in dict:
  print("blah")
else:
  print("boo")

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


7
Это великолепно. У меня сложилось впечатление, что он все еще будет проходить через список ключей, но я вижу, что это больше похоже на тестирование членства в наборе.
Мохан Гулати

51
@ Мохан Гулати: Вы понимаете, что словарь - это хеш-таблица ключей, сопоставленных со значениями, верно? Алгоритм хеширования преобразует ключ в целое число, а целое число используется для поиска местоположения в хеш-таблице, которое соответствует. ru.wikipedia.org/wiki/Hash_table
hughdbrown

5
@Charles Addis, из опыта работы с примерно полмиллиона ключей вы получаете увеличение производительности как минимум в 10 раз при написании «ключ в dict» вместо «ключ в dict.keys ()». PEP и Zen также заявляют, что вы должны игнорировать их, если они вредны для вашего проекта.
ivan_bilan

11
ivan_bilan - я только что провёл свой собственный тест на этом ... На полмиллиона ключей if key in d1ушло 0.17265701293945312несколько секунд. Вызов if key in d1.keys()занял 0.23871088027954102- это классическое определение микрооптимизации. Экономия 0.07884883880615234секунд - это не повышение производительности.
Чарльз Аддис

11
@Eli Только для вас я создал тест, который вы можете запустить самостоятельно. Результаты могут вас удивить. Для диктовок с ~ 50 000 ключей отсутствие вызова keys()дает вам 0,01 секунды вычислительной выгоды. За ~ 500 000 ключей отказ от звонка keys()дает вам .1 второе преимущество. Для ~ 5 000 000 ключей, не звонить keys()на 0,4 секунды быстрее, но для 50 000 000 ключей ВЫЗОВ - НА keys()3 СЕКУНДЫ БЫСТРЕЕ!
Чарльз Аддис

268

Вы можете проверить наличие ключа в словаре, используя ключевое слово in :

d = {'a': 1, 'b': 2}
'a' in d # <== evaluates to True
'c' in d # <== evaluates to False

Обычное использование для проверки существования ключа в словаре перед его изменением - инициализация значения по умолчанию (например, если ваши значения являются списками, и вы хотите убедиться, что существует пустой список, к которому вы можете добавить при вставке первого значения для ключа). В таких случаях вы можете найти collections.defaultdict()интересующий вас тип.

В старом коде вы также можете найти некоторые способы has_key()использования устаревшего метода для проверки наличия ключей в словарях (просто используйте key_name in dict_nameвместо этого).


2
Хотел поделиться этим (используя Python 2.7), время выполнения чего-то, что я только что написал, в значительной степени опираясь на dicts, было 363.235070 с использованием «key in dict.keys ()» и резко упало до 0.260186 исключительно путем удаления вызова для ключей ( ) "
Ido_f

@Ido_f, пожалуйста, опубликуйте ваши тесты, так как мои тесты почти не имеют различий в 3,5 и 2,7
Чарльз Аддис

@Ido_f Я подозреваю, что в вашей программе было что-то еще, но не совсем key in dict.keys(). Попробуйте удалить весь код, кроме этой проверки, и посмотрите, каков ваш результат.
Чарльз Аддис

101

Вы можете сократить это:

if 'key1' in dict:
    ...

Однако это в лучшем случае косметическое улучшение. Почему вы считаете, что это не самый лучший способ?


100
Это гораздо больше, чем косметическое улучшение. Время нахождения ключа с использованием этого метода составляет O (1), тогда как вызывающие ключи генерируют список и имеют значение O (n).
Джейсон Бейкер

5
О (1) не совсем верно. Вы уверены, что это не что-то вроде O (log n)?
спектры

12
Это сложность одиночного поиска, который в среднем равен O (1), а в худшем - O (n). .list () всегда будет O (n). wiki.python.org/moin/TimeComplexity
Леонора Тиндалл

1
это также позволяет избежать дополнительного распределения. (важно для того, чтобы сделать крутые петли немного быстрее)
nurettin

57

Для получения дополнительной информации о быстром выполнении предложенного метода предложенного ответа (петли 10 м):

  • 'key' in mydict прошедшее время 1,07 сек
  • mydict.get('key') прошедшее время 1,84 сек
  • mydefaultdict['key'] прошедшее время 1,07 сек

Поэтому используйте inили defaultdictрекомендуется против get.


6
полностью согласен, что get1.84s <1.07 * 2 ;-P
Пол Ригор

54

Я бы рекомендовал использовать setdefaultметод вместо. Похоже, он будет делать все, что вы хотите.

>>> d = {'foo':'bar'}
>>> q = d.setdefault('foo','baz') #Do not override the existing key
>>> print q #The value takes what was originally in the dictionary
bar
>>> print d
{'foo': 'bar'}
>>> r = d.setdefault('baz',18) #baz was never in the dictionary
>>> print r #Now r has the value supplied above
18
>>> print d #The dictionary's been updated
{'foo': 'bar', 'baz': 18}

9
Что setdefaultобщего с вопросом ОП?
hughdbrown

18
@hughdbrown "Я хотел проверить, существует ли ключ в словаре, прежде чем обновлять значение ключа." Иногда посты включают код, который генерирует поток ответов на что-то, что не совсем первоначальная цель. Для достижения цели, указанной в первом предложении, setdefault является наиболее эффективным методом, хотя он и не является заменой размещенного примера кода.
Дэвид Бергер

5
Это лучший ответ, потому что он удовлетворяет цели ОП, а не просто дает технически правильный ответ. Смотрите: nedbatchelder.com/blog/201207/…
Нильс Бом,

+1 за информативный ответ, который меня чему-то научил. Однако, будет ли это лучшим решением, зависит от того, что имеет в виду кодер; например, значение «перед обновлением значения ключа». Возможно, он собирается сгенерировать исключение, если оно отсутствует (== нет разрешения на добавление новых ключей). Возможно, это словарь счетчиков, и он собирается добавить 1 к существующему счету, и в этом случае `d [key] = d.get (key, 0) + 1 '- самое чистое решение (как показывает Крис после вашего ответа) было написано). (Я упоминаю об этом только в случае, если сюда придут будущие читатели с разными задачами.)
ToolmakerSteve

1
@ToolmakerSteve Правда. Проблема здесь в том, что вопрос ОП был недостаточно ясным.
Нильс Бом

45

Словарь в python имеет метод get ('key', default). Таким образом, вы можете просто установить значение по умолчанию, если нет ключа.

values = {...}
myValue = values.get('Key', None)

33

Как насчет использования EAFP (проще просить прощения, чем разрешения):

try:
   blah = dict["mykey"]
   # key exists in dict
except KeyError:
   # key doesn't exist in dict

Смотрите другие SO сообщения:

Используя try vs if в python или

Проверка существования члена в Python


12
Попытка / исключение может быть более дорогой, если вероятность того, что ключ часто не существует. Из поста, на который вы ссылались: «Если вы ожидаете, что результат 99% времени будет действительно содержать что-то итеративное, я бы использовал подход« попробуй / кроме ». Это будет быстрее, если исключения действительно исключительные. Если результат - Нет» более чем в 50% случаев, тогда, вероятно, лучше использовать if. [...] [A] n если оператор всегда стоит вас, то почти свободно можно установить блок try / исключением. Но когда действительно возникает исключение, стоимость намного выше ". stackoverflow.com/a/1835844/1094092
billrichards

28

Использование троичного оператора:

message = "blah" if 'key1' in dict else "booh"
print(message)

20

Способы, которыми вы можете получить результаты:

  • если your_dict.has_key (key) удален в Python 3
  • если введите your_dict
  • попробуй / кроме блока

Что лучше, зависит от 3 вещей:

  1. Есть ли в словаре «обычно ключ» или «обычно нет ключа».
  2. Намереваетесь ли вы использовать такие условия, как если ... еще ... еще ... еще?
  3. Насколько большой словарь?

Подробнее: http://paltman.com/try-except-performance-in-python-a-simple-test/

Использование try / block вместо «in» или «if»:

try:
    my_dict_of_items[key_i_want_to_check]
except KeyError:
    # Do the operation you wanted to do for "key not present in dict".
else:
    # Do the operation you wanted to do with "key present in dict."

2
Хорошо, но нужно актуализировать для python 3. Я преобразовал скрипт веб-страницы с помощью 2to3и увидел, что синтаксис без try всегда быстрее, чем синтаксис с try, даже в случае, когда ключ находится в dict.
Жан Поль

18

Только Python 2: (и Python 2.7 inуже поддерживает )

Вы можете использовать метод has_key ():

if dict.has_key('xyz')==1:
    #update the value for the key
else:
    pass

22
.has_key()было устаревшим ; Вы должны использовать, inкак показано в других ответах.
Брэд Кох

12
Кстати, я рекомендую прочитать ВСЕ существующие ответы на старый вопрос, прежде чем отвечать на него. Этот ответ ничего не добавил, так как предложение уже существовало в ответе Майкла от 09 года. (Я не хочу препятствовать попытке добавить что-то полезное к обсуждению. Продолжайте пытаться.)
ToolmakerSteve

16

Просто добавление к Крису. B (лучший ответ):

d = defaultdict(int)

Работает так же; причина в том, что вызов int()return, 0что и defaultdictпроисходит за кулисами (при создании словаря), отсюда и название «Factory Function» в документации.


2
Если вы создаете словарь счетчиков, вы должны использовать Counter (при условии Python 2.7). И я использовал defaultdict(lambda: 0)вместо, defaultdict(int)потому что я думаю, что яснее, что происходит; читателю не нужно знать, что вы получите, 0если позвоните int()без аргументов. YMMV.
Крис Б.

9

Проверьте, существует ли данный ключ в словаре

Чтобы понять, как это сделать, мы сначала проверим, какие методы мы можем вызвать в словаре. Вот методы:

d={'clear':0, 'copy':1, 'fromkeys':2, 'get':3, 'items':4, 'keys':5, 'pop':6, 'popitem':7, 'setdefault':8, 'update':9, 'values':10}

Python Dictionary clear()       Removes all Items
Python Dictionary copy()        Returns Shallow Copy of a Dictionary
Python Dictionary fromkeys()    Creates dictionary from given sequence
Python Dictionary get()         Returns Value of The Key
Python Dictionary items()       Returns view of dictionary (key, value) pair
Python Dictionary keys()        Returns View Object of All Keys
Python Dictionary pop()         Removes and returns element having given key
Python Dictionary popitem()     Returns & Removes Element From Dictionary
Python Dictionary setdefault()  Inserts Key With a Value if Key is not Present
Python Dictionary update()      Updates the Dictionary 
Python Dictionary values()      Returns view of all values in dictionary

Жестоким методом проверки того, существует ли ключ, может быть get()метод:

d.get("key")

Два других интересных метода items()и keys()звучит как слишком много работы. Итак, давайте рассмотрим, get()является ли правильный метод для нас. У нас есть наш дикт d:

d= {'clear':0, 'copy':1, 'fromkeys':2, 'get':3, 'items':4, 'keys':5, 'pop':6, 'popitem':7, 'setdefault':8, 'update':9, 'values':10}

Печать показывает, что ключ, которого у нас нет, вернется None:

print(d.get('key')) #None
print(d.get('clear')) #0
print(d.get('copy')) #1

Мы можем использовать это, чтобы получить информацию, если ключ присутствует или нет. Но учтите это, если мы создадим dict с одним key:None:

d= {'key':None}
print(d.get('key')) #None
print(d.get('key2')) #None

Ведущий get() методом не является надежным в случае, если некоторые значения могут быть None. У этой истории должен быть более счастливый конец. Если мы используем inкомпаратор:

print('key' in d) #True
print('key2' in d) #False

Мы получаем правильные результаты. Мы можем изучить байт-код Python:

import dis
dis.dis("'key' in d")
#   1           0 LOAD_CONST               0 ('key')
#               2 LOAD_NAME                0 (d)
#               4 COMPARE_OP               6 (in)
#               6 RETURN_VALUE

dis.dis("d.get('key2')")
#   1           0 LOAD_NAME                0 (d)
#               2 LOAD_METHOD              1 (get)
#               4 LOAD_CONST               0 ('key2')
#               6 CALL_METHOD              1
#               8 RETURN_VALUE

Это показывает, что inоператор сравнения не просто более надежен, но даже быстрее, чем get().


.get()может иметь второй аргумент для defaultзначения, который может быть использован для решения проблемы, где key:None. пример: d.get("key", False)
Алекс

.get()это самый быстрый способ. Другим вариантом является назначение в try/ exceptблок
HCLivess

7

В словаре Python есть метод, который называется __contains__. Этот метод возвращает True, если в словаре есть ключ, иначе возвращается False.

 >>> temp = {}

 >>> help(temp.__contains__)

Help on built-in function __contains__:

__contains__(key, /) method of builtins.dict instance
    True if D has a key k, else False.

2
Звонить __contains__напрямую - очень плохая практика . Правильный способ сделать это, это использовать inоператор, который containment checkвызывает __contains__функцию.
user1767754

@ user1767754 Я использую foo = x['foo'] if x.__contains__('foo') else 'bar'. Любые идеи, как бы использовать inоператор как часть этого выражения?
Донрондадон

1
foo = x['foo'] if 'foo' in x else 'bar'
Рэй Ву

5

Совместное использование еще одного способа проверки наличия ключа с использованием логических операторов.

d = {'a': 1, 'b':2}
keys = 'abcd'

for k in keys:
    x = (k in d and 'blah') or 'boo'
    print(x) 

Это возвращает

>>> blah
>>> blah
>>> boo
>>> boo

объяснение

Во- первых , вы должны знать , что в Python, 0, Noneили объекты с нулевой длиной вычисляться False. Все остальное оценивается как True. Булевы операции оцениваются слева направо и возвращают операнд не True или False.

Давайте посмотрим на пример:

>>> 'Some string' or 1/0 
'Some string'
>>>

Так как 'Some string'оценивается как True, остальная часть orне оценивается, и нет деления на ноль возникших ошибок.

Но если мы переключаемся, порядок 1/0оценивается первым и вызывает исключение:

>>> 1/0 or 'Some string'
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ZeroDivisionError: division by zero
>>> 

Мы можем использовать это для шаблона для проверки, существует ли ключ.

(k in d and 'blah')

делает так же, как

if k in d:
    'blah'
else:
    False

Это уже возвращает правильный результат, если ключ существует, но мы хотим, чтобы он печатал 'boo', когда его нет. Итак, мы берем результат и orего'boo'

>>> False or 'boo'
'boo'
>>> 'blah' or 'boo'
'blah'
>>> 

1

Вы можете использовать forцикл для перебора словаря и получить имя ключа, который вы хотите найти в словаре, после этого проверьте, существует ли он или нет, используя ifусловие:

dic = {'first' : 12, 'second' : 123}
for each in dic:
    if each == 'second': 
        print('the key exists and the corresponding value can be updated in the dictionary')

проверьте код, потому что вывод для этого it is existиnot exist
system123456

зачем вообще использовать словарь, если это для выполнения линейного поиска?
Жан-Франсуа Фабр
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.