Verifique se uma chave já existe em um dicionário


2683

Eu queria testar se existe uma chave em um dicionário antes de atualizar o valor da chave. Eu escrevi o seguinte código:

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

Eu acho que essa não é a melhor maneira de realizar essa tarefa. Existe uma maneira melhor de testar uma chave no dicionário?


31
A chamada dict.keys()cria uma lista de chaves, de acordo com a documentação docs.python.org/2/library/stdtypes.html#dict.keys, mas eu ficaria surpreso se esse padrão não fosse otimizado para, em uma implementação séria, traduzir para if 'key1' in dict:.
Evgeni Sergeev 12/08

7
Então, eu finalmente descobri por que muitos dos meus scripts Python foram tão lento :) :(. Isso é porque eu tenho usado x in dict.keys()para verificar as chaves. E isso aconteceu porque a maneira usual para iterar sobre as chaves em Java é for (Type k : dict.keySet()), este hábito causando for k in dict.keys()a se sentir mais natural do que for k in dict, mas depois as teclas verificação torna-se (que ainda deve ser muito bem em termos de desempenho?) if k in dict.keys()também, que é um problema ...
Evgeni Sergeev

4
O @EvgeniSergeev if k in dict_:testa a presença de k nas CHAVES do dict_, portanto você ainda não precisa dict_.keys(). (Isto tem pouco me, como se lê para mim como seu teste para um valor em dict Mas não é..)
ToolmakerSteve

1
@ToolmakerSteve Isso mesmo, mas não apenas você não precisa, como também não é uma boa prática.
Evgeni Sergeev

26
Tente "key in dict"
marcelosalloum 26/06

Respostas:


3373

iné a maneira pretendida de testar a existência de uma chave em a dict.

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

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

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

Se você queria um padrão, sempre pode usar dict.get():

d = dict()

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

e se você sempre quis garantir um valor padrão para qualquer chave, pode usar dict.setdefault()repetidamente ou defaultdictno collectionsmódulo, da seguinte maneira:

from collections import defaultdict

d = defaultdict(int)

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

mas, em geral, a inpalavra-chave é a melhor maneira de fazer isso.


74
Normalmente, uso apenas getse vou retirar o item do dicionário de qualquer maneira. Não faz sentido usar in e puxar o item para fora do dicionário.
21412 Jason Baker

75
Eu concordo plenamente. Mas se você só precisa saber se existe uma chave ou se precisa distinguir entre um caso em que a chave está definida e um caso em que você está usando um padrão, iné a melhor maneira de fazê-lo.
Chris B.

5
A referência para esta resposta está nos documentos python
enkash

30
get é um teste ruim se a chave for equivalente a "False", como 0por exemplo. Aprendi isso da maneira mais difícil: /
Sebastien

4
Não posso concordar que essa seja uma resposta completa, pois não menciona que 'try' - 'except' será o mais rápido quando o número de falhas de chave for suficientemente pequeno. Veja esta resposta abaixo: stackoverflow.com/a/1602945/4376643
Craig Hicks

1547

Você não precisa chamar as teclas:

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

Isso será muito mais rápido , pois usa o hash do dicionário em vez de fazer uma pesquisa linear, o que as teclas de chamada fariam.


7
Isso é ótimo. Fiquei com a impressão de que ele ainda atravessaria a lista de chaves internamente, mas vejo que isso funciona mais como testar a associação em um conjunto.
Mohan Gulati

51
@ Mohan Gulati: Você entende que um dicionário é uma hashtable de chaves mapeadas para valores, certo? Um algoritmo de hash converte a chave em um número inteiro e o número inteiro é usado para encontrar um local na tabela de hash que corresponda. en.wikipedia.org/wiki/Hash_table
hughdbrown 22/10/09

5
@Charles Addis, da experiência de trabalhar com cerca de meio milhão de chaves, você obtém pelo menos 10x aumento de desempenho ao escrever "key in dict" em vez de "key in dict.keys ()". O PEP e o Zen também afirmam que você deve ignorá-los caso sejam prejudiciais ao seu projeto.
ivan_bilan

11
ivan_bilan - Acabei de executar meu próprio teste neste ... Em meio milhão de chaves, if key in d1levou 0.17265701293945312segundos. A chamada if key in d1.keys()levou 0.23871088027954102- esta é a definição clássica de uma micro-otimização. Economizar 0.07884883880615234segundos não é um aumento de desempenho.
Charles Addis

11
@ Eli Apenas para você eu criei um teste que você pode executar sozinho. Os resultados podem surpreendê-lo. Para dictos com ~ 50.000 chaves, não chamar keys()fornece 0,01 segundo de benefício computacional. Para ~ 500.000 chaves, não ligar keys()oferece um benefício de 0,1 segundo. Para ~ 5.000.000 de chaves, a não chamada keys()é 0,4 segundos mais rápida, mas para 50.000.000 de chaves, CHAMAR keys()É 3 SEGUNDOS MAIS RÁPIDO!
Charles Addis

268

Você pode testar a presença de uma chave em um dicionário usando a palavra-chave in :

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

Um uso comum para verificar a existência de uma chave em um dicionário antes de alterá-la é inicializar o valor por padrão (por exemplo, se seus valores são listas, por exemplo, e você deseja garantir que exista uma lista vazia à qual possa anexar ao inserir o primeiro valor para uma chave). Em casos como esses, você pode achar que o collections.defaultdict()tipo é do seu interesse.

No código antigo, você também pode encontrar alguns usos de has_key(), um método obsoleto para verificar a existência de chaves nos dicionários (basta usar key_name in dict_name).


2
Queria compartilhar que (usando Python 2.7) o tempo de execução de algo que acabei de escrever, baseando-se fortemente nos dict, era 363.235070 usando "key in dict.keys ()" e diminuiu drasticamente para 0.260186 apenas removendo a chamada por "keys ( ) "
Ido_f 26/01

@Ido_f Por favor, postar seus benchmarks, como meus benchmarks quase nenhuma diferença em 3,5 e 2,7
Charles Addis

@ Ido_f Eu suspeito que havia algo mais no seu programa que era outra coisa, mas na verdade não key in dict.keys(). Tente remover todo o código, exceto esta verificação, e veja qual é o seu resultado.
Charles Addis

101

Você pode encurtar isso:

if 'key1' in dict:
    ...

No entanto, isso é, na melhor das hipóteses, uma melhoria cosmética. Por que você acredita que esse não é o melhor caminho?


100
Isso é muito mais do que uma melhoria estética. O tempo para encontrar uma chave usando esse método é O (1), enquanto as chaves de chamada gerariam uma lista e seriam O (n).
21330 Jason Baker

5
O (1) não parece muito certo. Tem certeza de que não é algo como O (log n)?
espectros

12
É a complexidade de uma única pesquisa de ditado, que é em média O (1) e, na pior das hipóteses, O (n). .list () sempre será O (n). wiki.python.org/moin/TimeComplexity
Leonora Tindall

1
isso também evita uma alocação extra. (importante para fazer apertado loops de um pouco mais rápido)
nurettin

56

Para informações adicionais sobre a velocidade de execução dos métodos propostos da resposta aceita (10m loops):

  • 'key' in mydict tempo decorrido 1,07 seg
  • mydict.get('key') tempo decorrido 1,84 seg
  • mydefaultdict['key'] tempo decorrido 1,07 seg

Portanto, é recomendável usar inou defaultdictcontra get.


6
concordo totalmente que get's 1.84s é <1,07 * 2 ;-P
Paul Rigor

54

Eu recomendaria usar o setdefaultmétodo. Parece que fará tudo o que você quiser.

>>> 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
O que setdefaulttem a ver com a pergunta do OP?
hughdbrown

18
@hughdbrown "Eu queria testar se existe uma chave em um dicionário antes de atualizar o valor da chave." Às vezes, as postagens incluem código que gera uma enxurrada de respostas para algo que não é exatamente o objetivo original. Para atingir a meta declarada na primeira frase, o setdefault é o método mais eficaz, mesmo que não seja um substituto para o código de amostra publicado.
21711 David Berger

5
Essa é a resposta superior, pois satisfaz a meta do OP em vez de apenas fornecer a resposta tecnicamente correta. Veja: nedbatchelder.com/blog/201207/…
Niels Bom

+1 para uma resposta informativa, que me ensinou alguma coisa. No entanto, se é a melhor solução depende do que o codificador tiver em mente; por exemplo, o significado de "antes de atualizar o valor da chave". Talvez ele gere uma exceção se ela não estiver presente (== nenhuma permissão para adicionar novas chaves). Talvez seja um dicionário de contagens, e ele adicionará 1 à contagem existente. Nesse caso, `d [chave] = d.get (chave, 0) + 1 'é a solução mais limpa (como Chris mostra, após sua resposta foi escrito). (Eu só se preocupar mencionar isso, em futuros leitores de casos vêm aqui, com tarefas diferentes em mente.)
ToolmakerSteve

1
@ToolmakerSteve True. O problema aqui é que a pergunta do OP não era clara o suficiente.
Niels Bom

45

O dicionário em python possui um método get ('key', padrão). Portanto, você pode apenas definir um valor padrão, caso não haja chave.

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

33

Que tal usar o EAFP (é mais fácil pedir perdão do que permissão):

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

Veja outras postagens do SO:

Usando try vs if em python ou

Verificando a existência de membros em Python


12
Tente / exceto pode ser mais caro se é provável que a chave geralmente não exista. No post que você referenciou: "[se você espera que 99% do resultado do tempo realmente contenha algo iterável, eu usaria a abordagem try / except. Será mais rápido se as exceções forem realmente excepcionais. Se o resultado for None mais de 50% do tempo, então usar if é provavelmente melhor. [...] [A] n se a instrução sempre lhe custar, é quase livre configurar um bloco try / except. Mas quando uma exceção realmente ocorre, o o custo é muito maior ". stackoverflow.com/a/1835844/1094092
billrichards

28

Usando operador ternário:

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

20

As maneiras pelas quais você pode obter os resultados são:

O que é melhor depende de três coisas:

  1. O dicionário 'normalmente possui a chave' ou 'normalmente não possui a chave'.
  2. Você pretende usar condições como se ... else ... elseif ... else?
  3. Qual é o tamanho do dicionário?

Leia mais: http://paltman.com/try-except-performance-in-python-a-simple-test/

Uso de try / block em vez de 'in' ou '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
Bom, mas precisa ser atualizado para o python 3. Convertei o script da página da Web 2to3e vi que a sintaxe sem tentativa é sempre mais rápida que a sintaxe com tentativa, mesmo no caso em que a chave está no ditado.
31419 Jean Paul

18

Somente Python 2: (e o python 2.7 injá suporta )

você pode usar o método has_key ():

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

22
.has_key()foi descontinuado ; você deve usar incomo mostrado em outras respostas.
Brad Koch

12
BTW, eu recomendo a leitura de todas as respostas existentes para uma pergunta antiga , antes de responder. Essa resposta não acrescentou nada, já que a sugestão já existia na resposta de Michael, a partir de '09. (Não me refiro a desencorajar uma tentativa de adicionar algo útil para uma discussão Continue tentando..)
ToolmakerSteve

16

Apenas um FYI adicionando a Chris. B (melhor resposta):

d = defaultdict(int)

Funciona também; a razão é que a chamada int()retorna, o 0que defaultdictocorre nos bastidores (ao construir um dicionário), daí o nome "Factory Function" na documentação.


2
Se você estiver criando um dicionário de contagens, deverá usar o Counter (assumindo o Python 2.7). E usei em defaultdict(lambda: 0)vez de defaultdict(int)porque acho mais claro o que está acontecendo; o leitor não precisa saber o que você recebe 0se ligar int()sem argumentos. YMMV.
Chris B.

9

Verifique se uma chave já existe em um dicionário

Para ter uma idéia de como fazer isso, primeiro inspecionamos quais métodos podemos chamar no dicionário. Aqui estão os métodos:

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

O método brutal para verificar se a chave já existe pode ser o get()método:

d.get("key")

Os outros dois métodos interessantesitems() e keys()parecem muito trabalho. Então, vamos examinar se get()é o método certo para nós. Temos o nosso ditado d:

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

A impressão mostra a chave que não temos retornará None:

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

Nós podem usar isso para obter a informação se a chave está presente ou não. Mas considere isso se criarmos um ditado com um único key:None:

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

Liderar esse get()método não é confiável, caso alguns valores possam ser None. Esta história deve ter um final mais feliz. Se usarmos o incomparador:

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

Nós obtemos os resultados corretos. Podemos examinar o código de bytes do 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

Isso mostra que o inoperador de comparação não é apenas mais confiável, mas ainda mais rápido que get().


.get()pode ter um segundo argumento para defaultvalor, que poderia ser usado para lidar com o problema em que key:None. exemplo: d.get("key", False)
Alex

.get()é o caminho mais rápido. Outra opção é atribuir em um bloco try/except
HCLivess

7

Dicionário Python tem o método chamado __contains__. Este método retornará True se o dicionário tiver a chave else retorna 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
É uma prática muito ruim ligar __contains__diretamente. A maneira correta de fazer isso é usar o inoperador, que é o containment checkque chama a __contains__função.
usar o seguinte comando

@ user1767754 estou usando foo = x['foo'] if x.__contains__('foo') else 'bar'. Alguma idéia de como poderia usar o inoperador como parte dessa expressão?
donrondadon

1
foo = x['foo'] if 'foo' in x else 'bar'
Ray Wu

5

Compartilhando mais uma maneira de verificar se existe uma chave usando operadores booleanos.

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

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

Isso retorna

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

Explicação

Primeiro você deve saber que em Python, 0, None, ou objetos com comprimento zero avaliar a False. Tudo o resto é avaliado True. As operações booleanas são avaliadas da esquerda para a direita e retornam o operando não True ou False.

Vamos ver um exemplo:

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

Como 'Some string'avalia para True, o restante do ornão é avaliado e não há divisão por erro zero gerado.

Mas se mudarmos, a ordem 1/0é avaliada primeiro e gera uma exceção:

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

Podemos usar isso como padrão para verificar se existe uma chave.

(k in d and 'blah')

faz o mesmo que

if k in d:
    'blah'
else:
    False

Isso já retorna o resultado correto se a chave existir, mas queremos que ela imprima 'vaia' quando não existir. Então, pegamos o resultado e orele com'boo'

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

1

Você pode usar forloop para iterar sobre o dicionário e obter o nome da chave que deseja encontrar no dicionário; depois disso, verifique se existe ou não usando a ifcondição:

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')

verifique o código porque a saída para isso é it is existenot exist
system123456 18/04/19

por que usar um dicionário para realizar pesquisa linear?
Jean-François Fabre
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.