El diccionario es una de las estructuras de datos más utilizadas en Python. Un diccionario es una colección desordenada de elementos. A menudo, tenemos claves y valores almacenados en un diccionario. Veamos algunos ejemplos de cómo se usa el diccionario.

# declaración de diccionario 1
dict1 = dict()

# declaración de diccionario 2
dict2 = {}

# Añadir items al diccionario
# La sintaxis para añadir y recuperar elementos es la misma para los dos objetos que hemos definido anteriormente. 
key = "X"
value = "Y"
dict1[key] = value

# El diccionario no tiene un tipo especifico de datos. 
# Entonces, los valores pueden ser muy diversos. 
dict1[key] = dict2

Veamos ahora algunas formas de recuperar la información.

# Desde que existe "X" en nuestro diccionario, esta sentencia nos devolverá el valor
value = dict1[key]

# Esta clave no existe en el diccionario. 
# Entonces, esto devolverá un `KeyError`
value = dict1["random"]

Evitando el KeyError: Utiliza la función .get

En caso de que la clave proporcionada no exista en el diccionario, Python lanzará un  KeyError. Hay una solución sencilla. Veamos como podemos evitar el  KeyError utilizando la función  .get incluida en los diccionarios.

dict_ = {}

# Una clave aleatoria
random_key = "random"

# La manera más sencilla de hacer esto es comprobar que existe la clave 
# en el diccionario o no y solo recupera la clave si existe. 
# En caso contrario no. 
if random_key in dict_:
  print(dict_[random_key])
else:
  print("Key = {} doesn't exist in the dictionary".format(dict_))

Muchas veces podemos obtener un valor predeterminado cuando la clave no existe. Por ejemplo al construir un contador. Existe una manera mejor de obtener valores predeterminados del diccionario en caso de que falten claves en lugar de confiar en el if-else estándar.

# Digamos que queremos construir un contador de cuantas veces aparece un elemento en el siguiente arreglo
arr = [1,2,3,1,2,3,4,1,2,1,4,1,2,3,1]

freq = {}

for item in arr:
  # Trae un valor para 0 en caso de que no exista. En caso contrario trae el valor almacenado
  freq[item] = freq.get(item, 0) + 1

Entonces,  get(<key>, <defaultval>) es una operación útil para recuperar el valor predeterminado para cualquier clave dada del diccionario. El problema con este método surge cuando queremos tratar con estructuras de datos mutables como  list o set.

dict_ = {}

# Alguna clave aleatoria
random_key = "random"

dict_[random_key] = dict_.get(random_key, []).append("Hello World!")
print(dict_) # {'random': None}

dict_ = {}
dict_[random_key] = dict_.get(random_key, set()).add("Hello World!")
print(dict_) # {'random': None}

¿Has visto el problema?

El nuevo set o la list no se asigna a la clave del diccionario. Debemos asignar una nueva list o un set a la clave en caso de que falte un valor y luego append (append) o add (añadir) respectivamente. Mira un ejemplo para esto.

dict_ = {}
dict_[random_key] = dict_.get(random_key, set())
dict_[random_key].add("Hello World!")
print(dict_) # {'random': set(['Hello World!'])}. Yay!

Evitar KeyError: Utiliza defaultdict

Esto funciona la mayoría de las veces. Sin embargo, existe una mejor forma de hacerlo. Una forma más pythónica. El defaultdict es una subclase de la clase dict incorporada. El defaultdict simplemente asigna el valor predeterminado que especificamos en caso de que falte una clave. Entonces, los dos pasos:

dict_[random_key] = dict_.get(random_key, set())
dict_[random_key].add("Hello World!")

Ahora se pueden combinar en un único paso. Por ejemplo:

rom collections import defaultdict

# Otra clave aleatoria
random_key = "random_key"

# list como defaultdict
list_dict_ = defaultdict(list)

# set como defaultdict
set_dict_ = defaultdict(set)

# integer como defaultdict
int_dict_ = defaultdict(int)

list_dict_[random_key].append("Hello World!")
set_dict_[random_key].add("Hello World!")
int_dict_[random_key] += 1

"""
  defaultdict(<class 'list'>, {'random_key': ['Hello World!']}) 
  defaultdict(<class 'set'>, {'random_key': {'Hello World!'}}) 
  defaultdict(<class 'int'>, {'random_key': 1})
"""
print(list_dict_, set_dict_, int_dict_)

Traducido del artículo Python Dictionary Data Structure Explained