Verifique se uma chave já existe em um dicionário e aumente-a


295

Dado um dicionário, como posso descobrir se uma determinada chave nesse dicionário já foi definida como um valor diferente de Nenhum?

Ou seja, eu quero fazer isso:

my_dict = {}

if (my_dict[key] != None):
  my_dict[key] = 1
else:
  my_dict[key] += 1

Ou seja, eu quero incrementar o valor se já houver um lá ou configurá-lo para 1 caso contrário.


11
Pequeno código nitpick: o código define my_dict [key] como 1 se já houver algo lá e o incrementa se não houver. Eu acho que você quer ==, não! =.
— QuantumFool 23/06

Respostas:


331

Você está procurando collections.defaultdict(disponível para Python 2.5+). este

from collections import defaultdict

my_dict = defaultdict(int)
my_dict[key] += 1

fará o que você quiser.

Para Python regulares dict, se não houver valor para uma determinada chave, você não obterá Noneao acessar o dict - a KeyErrorserá gerado. Então, se você quiser usar um dictcódigo regular , em vez do seu código, você usaria

if key in my_dict:
    my_dict[key] += 1
else:
    my_dict[key] = 1

8
De acordo com seu exemplo, deve ser suficiente definir "defaultdict (lambda: 0)" e pular toda a cláusula "if".
— Deestan

Isso funciona, mas confunde chaves e valores (dificultando a leitura). 'some_value' deve ser 'some_key'
— mikemaccana

@ thumbnailer: fixo, obrigado. Inicialmente, usei 'some_value', já que esse é o nome da variável na pergunta, mas concordo que agora é mais claro.
— dF.

20
... ou para dicts regulares , você pode fazer my_dict[key] = my_dict.get(key, 0) + 1.
— minmaxavg

Como estender isso para dicionários aninhados? ditado [tecla1] [tecla2] + = 1?
— Pablo Ruiz Ruiz

301

Eu prefiro fazer isso em uma linha de código.

my_dict = {}

my_dict [some_key] = my_dict.get (alguma_key, 0) + 1

Os dicionários têm uma função, get, que aceita dois parâmetros - a chave desejada e um valor padrão, se não existir. Prefiro esse método ao padrão, já que você deseja lidar apenas com o caso em que a chave não existe nessa linha de código, não em todos os lugares.


1
@AndrewWilkinson my bad. Não leu sua resposta tão completamente quanto eu deveria.
— masaers

59

Eu pessoalmente gosto de usar setdefault()

my_dict = {}

my_dict.setdefault(some_key, 0)
my_dict[some_key] += 1

setdefaulté incrível. Não altera o valor se já estiver definido some_key. Por exemplo, d={1:2}; d.setdefault(1, 0)não perturba o valor de d[1].
— wsaleem

49

Você precisa do key in dictidioma para isso.

if key in my_dict and not (my_dict[key] is None):
  # do something
else:
  # do something else

No entanto, você provavelmente deve considerar o uso defaultdict(como sugerido pelo dF).


1
Observe que em pelo menos 2.6 has_key () foi excluído em favor da chave em d. Eu acho que foi assim também no 2.5.
— David Locke

Note-se que se pode escrever my_dict[key] is not None, que é mais clara (IMHO pelo menos)
— Brandizzi

@brandizzi - concordar,if key in my_dict and my_dict[key]:
— Rob Grant

18

Para responder à pergunta " como posso descobrir se um determinado índice nesse ditado já foi definido como um valor diferente de Nenhum ", eu preferiria isso:

try:
  nonNone = my_dict[key] is not None
except KeyError:
  nonNone = False

Isso está de acordo com o conceito já invocado de EAFP (mais fácil pedir perdão do que permissão). Também evita a pesquisa de chave duplicada no dicionário, como faria emkey in my_dict and my_dict[key] is not None interessante se a pesquisa fosse cara.

Para o problema real que você colocou, por exemplo, incrementar um int, se existir, ou defini-lo com um valor padrão, caso contrário, também recomendo

my_dict[key] = my_dict.get(key, default) + 1

como na resposta de Andrew Wilkinson.

Existe uma terceira solução se você estiver armazenando objetos modificáveis ​​no seu dicionário. Um exemplo comum disso é um multimap , onde você armazena uma lista de elementos para suas chaves. Nesse caso, você pode usar:

my_dict.setdefault(key, []).append(item)

Se um valor para chave não existir no dicionário, o método setdefault o definirá como o segundo parâmetro de setdefault. Ele se comporta exatamente como um padrão my_dict [key], retornando o valor da chave (que pode ser o novo valor definido).


o que parece na verdade Pythonic (para um estranho como eu) é que qualquer questão tem pelo menos 3 respostas válidas :)
— davka

@avka: Bem, os três casos de uso são quase os mesmos, mas diferentes: a) descubra se existe um elemento diferente de None no dicionário b) recupere um valor do dicionário ou use um padrão se o valor não existir c) recupere um valor do dicionário e armazene um padrão se o valor ainda não existir.
— nd.

Eu sei :) isto não é uma crítica, eu estou apenas divertindo-se com este fato
— davka

Em um comentário à resposta do @ ryeguy, Stuart Woodward sugere "a sobrecarga no tratamento de exceções nos idiomas é sempre uma ordem de magnitude maior que a pesquisa da tabela de hash que determina se o item existe ou não no dicionário", enquanto você está dizendo "Isso também evita a pesquisa de chave duplicada no dicionário ... se a pesquisa for cara "- alguém tem alguma medida de onde o tratamento de exceções é mais rápido ou mais lento do que uma pesquisa de chave dupla?
— Michael Firth

1
@ MichaelFirth Fiz uma pesquisa superficial da sobrecarga de exceção do Python: stackoverflow.com/questions/2522005/… é mais lenta, mas não muito. Lembre-se de que o conceito de alto nível de lançar uma exceção é tratado de maneira muito diferente em idiomas diferentes e você não pode generalizar os prós e os contras. Portanto, embora as "Exceções tenham uma sobrecarga de 10x" possam estar corretas para Java, não é para Python (ou Swift ou outros).
— nd.

13

Concordou com cgoldberg. Como eu faço isso é:

try:
    dict[key] += 1
except KeyError:
    dict[key] = 1

Portanto, faça-o como acima, ou use um ditado padrão, como outros sugeriram. Não use instruções if. Isso não é pitonico.


8
Como as declarações if não são pitonicas?
— quer

2
Eu acho que esse é um caso em que o EAFP do Python não é o melhor caminho. Seu exemplo acima tem código duplicado; e se um dia quisermos +=2ou -=1? Você precisa se lembrar de mudar as duas linhas. Pode parecer uma coisa trivial agora, mas esses são os tipos de insetos "triviais" estúpidos que podem voltar a morder você.
— Cam Jackson

3
Isso parece bom e funciona bem, mas geralmente evito fazê-lo assim, porque pensei que a sobrecarga no tratamento de exceções nos idiomas é sempre uma ordem de magnitude maior que a pesquisa da tabela de hash que determina se o item existe ou não no dicionário.
— Stuart Woodward

11

Como você pode ver nas muitas respostas, existem várias soluções. Uma instância do LBYL (veja antes de saltar) ainda não foi mencionada, o método has_key ():

my_dict = {}

def add (key):
    if my_dict.has_key(key):
        my_dict[key] += 1
    else:
        my_dict[key] = 1

if __name__ == '__main__':
    add("foo")
    add("bar")
    add("foo")
    print my_dict

6
has_key () é mais lento que o operador 'in' e é menos legível.
— Abgan

9
... e tem sido preterida no Python 2.6 e removido em Python 3.
— Tim Pietzcker

7

A maneira como você está tentando fazer isso é chamada LBYL (veja antes de pular), pois você está verificando as condições antes de tentar aumentar seu valor.

A outra abordagem é chamada EAFP (mais fácil pedir perdão do que permissão). Nesse caso, você apenas tentaria a operação (aumentaria o valor). Se falhar, você capturar a exceção e definir o valor como 1. Essa é uma maneira ligeiramente mais Pythonic de fazer isso (IMO).

http://mail.python.org/pipermail/python-list/2003-May/205182.html


5

Um pouco tarde, mas isso deve funcionar.

my_dict = {}
my_dict[key] = my_dict[key] + 1 if key in my_dict else 1

Uau, como programador Java, esse é um construto bem louco. Parece um operador ternário de ordem estranha?
— forresthopkinsa

5

Isso não está respondendo diretamente à pergunta, mas, para mim, parece que você pode querer a funcionalidade das coleções .

from collections import Counter

to_count = ["foo", "foo", "bar", "baz", "foo", "bar"]

count = Counter(to_count)

print(count)

print("acts just like the desired dictionary:")
print("bar occurs {} times".format(count["bar"]))

print("any item that does not occur in the list is set to 0:")
print("dog occurs {} times".format(count["dog"]))

print("can iterate over items from most frequent to least:")
for item, times in count.most_common():
    print("{} occurs {} times".format(item, times))

Isso resulta na saída

Counter({'foo': 3, 'bar': 2, 'baz': 1})
acts just like the desired dictionary:
bar occurs 2 times
any item that does not occur in the list is set to 0:
dog occurs 0 times
can iterate over items from most frequent to least:
foo occurs 3 times
bar occurs 2 times
baz occurs 1 times

O contador funciona da mesma maneira que defaultdict(int)com algumas funcionalidades extras, por isso funcionaria perfeitamente ao lidar exclusivamente com números inteiros, mas você não mostra nenhum comportamento relevante.
— Tadhg McDonald-Jensen

4

Aqui está uma frase que eu inventei recentemente para resolver esse problema. É baseado no método de dicionário setdefault :

my_dict = {}
my_dict[key] = my_dict.setdefault(key, 0) + 1

0

Eu estava procurando, não encontrei na web, tentei a sorte com Try / Error e encontrei

my_dict = {}

if my_dict.__contains__(some_key):
  my_dict[some_key] += 1
else:
  my_dict[some_key] = 1

1
Você não deveria estar usando __contains__em um código de produção. btw. __contains__é o mesmo que usar is.
— user1767754

1
my_dict.__contains__(some_key)é equivalente a some_key in my_dict, é sobrecarga para o inoperador nãois
— Tadhg McDonald-Jensen
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.