Перевірте, чи вказаний ключ вже існує в словнику


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
Спробуйте "ключ у
дикті

Відповіді:


3369

in- це призначений спосіб перевірити наявність ключа в a 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
Довідку на цю відповідь можна знайти в документах python
enkash

30
get - це поганий тест, якщо ключ еквівалентний "False", як, 0наприклад,. Навчився цьому важким шляхом: /
Себастьян

4
Я не можу погодитися, що ця повна відповідь, оскільки в ній не згадується, що "спробувати" - "за винятком" буде найшвидшим, коли кількість ключів не буде достатньою. Дивіться цю відповідь нижче: stackoverflow.com/a/1602945/4376643
Крейг Хікс

1546

Вам не потрібно дзвонити по клавішах:

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

Це буде набагато швидше, оскільки він використовує хешування словника на відміну від лінійного пошуку, який би виконував клавіші виклику.


7
Це чудово. У мене було враження, що він все ще перетинатиме список ключів, але я бачу, що це працює як тестування членства в наборі.
Мохан Гулаті

51
@Mohan Gulati: Ви розумієте, що словник - це хештейн ключів, відображених на значення, правда? Алгоритм хешування перетворює ключ на ціле число, а ціле число використовується для пошуку місця в хеш-таблиці, що відповідає. en.wikipedia.org/wiki/Hash_table
hughdbrown

5
@Charles Addis, завдяки досвіду роботи з близько півмільйона клавіш ви отримуєте принаймні 10-кратне підвищення продуктивності, коли пишете "ключ у dict" замість "key in 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()дає вам .01 секунду обчислювальну перевагу. Для ~ 500 000 клавіш не дзвінок keys()дає вам .1 секунду переваги. Для ~ 5 000 000 клавіш дзвінок keys()не на 4 секунди швидше, а на 50 000 000 клавіш, НАДАЙТЕ keys()3
Чарльз Аддіс

268

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

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) час роботи чогось, що я написав, базуючись на диктатах, склав 363,235070, використовуючи "ключ у 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
це також дозволяє уникнути додаткового виділення. (важливо для того, щоб трохи швидше зробити щільні петлі)
Нуреттін

56

Для отримання додаткової інформації про швидке виконання запропонованих методів відповіді (10 м циклів):

  • 'key' in mydict минув час 1,07 сек
  • mydict.get('key') минув час 1,84 сек
  • mydefaultdict['key'] минув час 1,07 сек

Тому використання inабо defaultdictрекомендується проти get.


6
повністю погоджуєтесь, що get1,84 '' <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/…
Niels Bom

+1 за інформативну відповідь, що мене чогось навчила. Однак, чи це найкраще рішення, залежить від того, що має на увазі кодер; наприклад, значення "перед оновленням значення ключа". Можливо, він збирається викинути виняток, якщо його немає (== немає дозволу на додавання нових ключів). Можливо, його словник підраховує, і він збирається додати 1 до існуючого рахунку; в цьому випадку `d [key] = d.get (клавіша, 0) + 1 'є найчистішим рішенням (як показує Кріс, після вашої відповіді був написаний). (Мені це лише згадує, якщо майбутні читачі приїдуть сюди, маючи на увазі різні завдання.)
ToolmakerSteve

1
@ToolmakerSteve True. Проблема тут полягає в тому, що питання ОП було недостатньо зрозумілим.
Нільс Бом

45

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

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:

Використання спробуйте vs якщо в python або

Перевірка існування члена в Python


12
Спробуйте / крім, можливо, буде дорожче, якщо ймовірно, що ключ часто не існує. З посилань, на які ви посилалися: "[I] Якщо ви очікуєте, що 99% результату часу насправді буде містити щось ітерабельне, я б скористався методом спробу / за винятком. Це буде швидше, якщо винятки справді виняткові. Якщо результат - немає більше 50% часу, то використовуючи, якщо, мабуть, краще. [...] [A] n якщо оператор завжди коштує вам, майже безкоштовно встановити спробу / крім блоку. Але коли виняток насправді відбувається, вартість набагато вища ". stackoverflow.com/a/1835844/1094092
billrichards

28

Використання потрійного оператора:

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

20

Способи отримання результатів:

  • якщо ваш_dict.has_key (ключ) видалено в Python 3
  • якщо ключ у вашому_виразу
  • спробуйте / крім блоку

Що краще, залежить від 3 речей:

  1. Чи в словнику "зазвичай є ключ" або "нормально немає ключа".
  2. Ви маєте намір використовувати такі умови, як, якби ... else ... elseif ... else?
  3. Наскільки великий словник?

Детальніше: http://paltman.com/try-except-performance-in-python-a-simple-test/

Використання спробувати / блокувати замість "в" або "якщо":

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і побачив, що синтаксис без спроб завжди швидший, ніж синтаксис спробуйте, навіть у випадку, коли ключ знаходиться в диктаті.
Жан Пол

18

Тільки Python 2: (і inвже підтримує python 2.7 )

ви можете використовувати метод 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()повертає 0те, що defaultdictвідбувається за лаштунками (при побудові словника), звідси і назва "Фабрична функція" в документації.


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

Ми можемо використовувати це для отримання інформації, якщо ключ присутній чи ні. Але врахуйте це, якщо ми створимо диктат із синглом 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__функцію.
користувач1767754

@ user1767754 Я використовую foo = x['foo'] if x.__contains__('foo') else 'bar'. Будь-які ідеї, як міг би використовувати inоператор як частину цього виразу?
donrondadon

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.