# Diccionarios en Python (dict): creación, métodos, recorrido, ordenación y frozendict

URL: https://skillaria.top/es/guides/python/diccionarios-python

Un diccionario en Python (dict) es una colección modificable de pares «clave — valor» que devuelve el valor por su clave casi al instante; se crea con llaves, por ejemplo {"café": 250}, o con la función dict(). Las claves son únicas y deben ser hashables; desde Python 3.7 se conserva el orden de inserción, y en Python 3.15 apareció la variante inmutable integrada, frozendict.

Un diccionario en Python (`dict`) es una colección de pares «clave — valor» en la que el valor se encuentra por su clave casi al instante, sin importar cuántos registros haya. Si necesita sumar los gastos por categoría, guardar la configuración o buscar un usuario por su id, use un diccionario. A continuación verá todo lo necesario para trabajar con `dict`: creación, métodos, recorrido, ordenación, unión y la novedad de Python 3.15, `frozendict`.

## Qué es un diccionario en Python

Un diccionario guarda pares en los que cada clave es única: si escribe un valor con una clave que ya existe, el anterior se reemplaza. Por dentro es una tabla hash, así que buscar, añadir y eliminar por clave funcionan en tiempo constante de media, sin recorrer todo el contenido.

La clave solo puede ser un objeto hashable: una cadena, un número o una tupla de valores inmutables. Una lista no puede ser clave y provoca `TypeError: unhashable type: 'list'`. Los valores, en cambio, pueden ser cualquier cosa, también otros diccionarios o listas.

Desde Python 3.7, el diccionario conserva con garantía el orden de inserción: al recorrerlo, las claves salen en el orden en que se añadieron.

## Cómo crear un diccionario

Hay varias formas de crear un diccionario, y todas aparecen en código real.

```
gastos = {"Comida": 1200, "Taxi": 450}      # literal
vacio = {}                                   # diccionario vacío
tambien_vacio = dict()                       # lo mismo con la función dict
usuario = dict(nombre="Ana", edad=30)        # argumentos con nombre
pares = dict([("a", 1), ("b", 2)])           # a partir de una lista de pares
claves = dict.fromkeys(["x", "y"], 0)        # {'x': 0, 'y': 0}
```

Fíjese en que las llaves vacías `{}` crean un diccionario vacío, no un conjunto. Un conjunto vacío se crea solo con `set()`.

Una forma cómoda de construir un diccionario a partir de dos listas es `zip`:

```
nombres = ["Comida", "Taxi", "Cine"]
importes = [1200, 450, 600]
gastos = dict(zip(nombres, importes))
print(gastos)   # {'Comida': 1200, 'Taxi': 450, 'Cine': 600}
```

## Acceso por clave: d[k] y get

El valor se lee con corchetes. Si la clave no existe, Python lanza `KeyError`. Para evitar el error, use el método `get`: devuelve `None` o el valor por defecto que indique.

```
gastos = {"Comida": 1200}
print(gastos["Comida"])            # 1200
print(gastos.get("Cine"))          # None
print(gastos.get("Cine", 0))       # 0
gastos["Cine"]                     # KeyError: 'Cine'
```

Para comprobar si una clave existe en el diccionario se usa el operador `in`: `"Comida" in gastos` devuelve `True`. Se comprueban las claves, no los valores; para buscar valores se escribe `1200 in gastos.values()`.

## Añadir, modificar y eliminar

Añadir o actualizar un registro es una sola línea de asignación. Para contar, resulta muy cómoda la combinación con `get`:

```
gastos = {}
for categoria, importe in [("Comida", 250), ("Taxi", 300), ("Comida", 400)]:
    gastos[categoria] = gastos.get(categoria, 0) + importe
print(gastos)   # {'Comida': 650, 'Taxi': 300}
```

Una clave se elimina con `del d[k]` (provoca `KeyError` si no existe) o con `d.pop(k, default)`, que además devuelve el valor. Para vaciar el diccionario por completo se usa `d.clear()`. El número de pares es `len(d)`.

## Métodos de los diccionarios: chuleta

| Método | Qué hace |
| --- | --- |

| `get(k, default)` | el valor de la clave, o default sin error |

| `setdefault(k, default)` | devuelve el valor; si no hay clave, primero escribe default |

| `update(otro)` | añade y sobrescribe los pares de otro diccionario |

| `pop(k, default)` | elimina la clave y devuelve el valor |

| `popitem()` | elimina y devuelve el último par añadido |

| `keys()`, `values()`, `items()` | vistas de las claves, los valores y los pares |

| `copy()` | copia superficial |

| `clear()` | vacía el diccionario |

| `fromkeys(claves, valor)` | nuevo diccionario con el mismo valor en todas las claves |

### setdefault: llenar un diccionario de listas

El método `setdefault` resulta útil cuando los valores son listas y hay que agrupar:

```
grupos = {}
for producto, cat in [("café", "Comida"), ("metro", "Transporte"), ("pizza", "Comida")]:
    grupos.setdefault(cat, []).append(producto)
print(grupos)   # {'Comida': ['café', 'pizza'], 'Transporte': ['metro']}
```

### update: actualizar un diccionario

`update` modifica el diccionario en su lugar: las claves nuevas se añaden y las coincidentes se sobrescriben.

```
config = {"debug": False, "idioma": "es"}
config.update({"debug": True, "tema": "oscuro"})
print(config)   # {'debug': True, 'idioma': 'es', 'tema': 'oscuro'}
```

## defaultdict: diccionario con valor por defecto

`collections.defaultdict` es un subtipo de dict que, al acceder a una clave inexistente, crea el valor mediante una función de fábrica. El código de recuentos y agrupaciones se vuelve más corto.

```
from collections import defaultdict

gastos = defaultdict(int)
for categoria, importe in [("Comida", 250), ("Taxi", 300), ("Comida", 400)]:
    gastos[categoria] += importe
print(dict(gastos))   # {'Comida': 650, 'Taxi': 300}

grupos = defaultdict(list)
grupos["Comida"].append("café")
```

Para contar apariciones existe además `collections.Counter`: `Counter(["a", "b", "a"])` devuelve `Counter({'a': 2, 'b': 1})`.

## Recorrer un diccionario con for

Al recorrer un diccionario con `for`, por defecto se obtienen las claves. Para obtener a la vez la clave y el valor, recorra `items()`; es la variante más habitual.

```
gastos = {"Comida": 650, "Taxi": 300}

for categoria in gastos:              # claves
    print(categoria)

for importe in gastos.values():       # valores
    print(importe)

for categoria, importe in gastos.items():   # pares
    print(f"{categoria}: {importe} €")
```

No se puede cambiar el tamaño del diccionario mientras se recorre: aparecerá `RuntimeError: dictionary changed size during iteration`. Si hay que eliminar algunas claves, recorra una copia: `for k in list(d):`.

## Mostrar un diccionario en pantalla

`print(d)` muestra el diccionario en una sola línea. Para diccionarios grandes o anidados resulta mejor el módulo `pprint` o `json.dumps(d, ensure_ascii=False, indent=2)`, que muestra los datos con sangría y sin escapar los caracteres acentuados.

## Ordenar un diccionario

El diccionario no se ordena en su lugar: no tiene método `sort`. Ordenar un diccionario significa crear otro a partir de sus pares ordenados con `sorted`.

```
gastos = {"Taxi": 300, "Comida": 650, "Cine": 120}

por_clave = dict(sorted(gastos.items()))
print(por_clave)    # {'Comida': 650, 'Cine': 120, 'Taxi': 300}

por_valor = dict(sorted(gastos.items(), key=lambda p: p[1], reverse=True))
print(por_valor)    # {'Comida': 650, 'Taxi': 300, 'Cine': 120}
```

Si solo necesita las N categorías principales, no vuelva a convertir el resultado en diccionario: `sorted(gastos.items(), key=lambda p: p[1], reverse=True)[:2]` devuelve una lista de pares.

Para ordenar una lista de diccionarios por un campo se escribe `sorted(usuarios, key=lambda u: u["edad"])`.

## Unir y sumar diccionarios

Desde Python 3.9, los diccionarios se unen con el operador `|`, y `|=` actualiza el diccionario en su lugar. Si las claves coinciden, gana el operando de la derecha.

```
a = {"x": 1, "y": 2}
b = {"y": 20, "z": 3}
print(a | b)          # {'x': 1, 'y': 20, 'z': 3}
print({**a, **b})     # lo mismo mediante desempaquetado
a |= b                # como a.update(b)
```

Tenga en cuenta que `a + b` no funciona con diccionarios: da `TypeError`. Si lo que quiere es sumar los valores de las claves coincidentes, use `Counter`: `Counter(a) + Counter(b)`.

### Desempaquetado de diccionarios

Dos asteriscos `**` desempaquetan un diccionario en argumentos con nombre de una función o en otro diccionario:

```
def saludar(nombre, ciudad):
    return f"{nombre} de {ciudad}"

datos = {"nombre": "Ana", "ciudad": "Valencia"}
print(saludar(**datos))   # Ana de Valencia
```

## Comprensión de diccionarios (dict comprehension)

Una comprensión de diccionarios construye un diccionario en una línea a partir de cualquier secuencia:

```
cuadrados = {n: n * n for n in range(5)}
print(cuadrados)   # {0: 0, 1: 1, 2: 4, 3: 9, 4: 16}

grandes = {k: v for k, v in gastos.items() if v > 200}

invertido = {v: k for k, v in {"a": 1, "b": 2}.items()}
print(invertido)   # {1: 'a', 2: 'b'}
```

El último ejemplo muestra cómo invertir un diccionario, intercambiando claves y valores. Funciona si los valores son únicos y hashables; de lo contrario, algunos pares se pierden.

## Diccionarios anidados

Un valor puede ser otro diccionario. Así se describen estructuras como las respuestas JSON de una API o un diccionario de diccionarios de usuarios.

```
usuarios = {
    101: {"nombre": "Ana", "habilidades": ["python", "sql"]},
    102: {"nombre": "Óscar", "habilidades": ["go"]},
}
print(usuarios[101]["habilidades"][0])            # python
print(usuarios.get(103, {}).get("nombre"))        # None, sin KeyError
```

## Copiar un diccionario: copia y segundo nombre

La sentencia `b = a` no copia el diccionario: pone un segundo nombre al mismo objeto. Si cambia algo a través de `b`, el cambio se ve también en `a`. Lo mismo ocurre al pasar un diccionario a una función: la función recibe una referencia al objeto compartido y puede estropearlo sin avisar.

```
import copy

a = {"limites": [0, 500]}
b = a.copy()              # copia superficial
b["limites"].append(1000) # ¡la lista anidada es común!
print(a)                  # {'limites': [0, 500, 1000]}

c = copy.deepcopy(a)      # copia completamente independiente
```

## Comparar diccionarios

El operador `==` compara los diccionarios por su contenido: son iguales si coinciden todos los pares. El orden de las claves no cuenta: `{"a": 1, "b": 2} == {"b": 2, "a": 1}` da `True`. Para encontrar las claves comunes, use las operaciones de conjuntos sobre las vistas: `a.keys() & b.keys()`.

## Diccionario tipado: anotaciones

La anotación de un diccionario se escribe como `dict[str, int]`. Si el diccionario tiene un conjunto fijo de campos de distintos tipos, resulta cómodo `TypedDict` del módulo `typing`:

```
from typing import TypedDict

class Usuario(TypedDict):
    nombre: str
    edad: int

u: Usuario = {"nombre": "Ana", "edad": 30}
```

Las anotaciones no se comprueban durante la ejecución: las leen los IDE y los analizadores como mypy.

## Diccionario inmutable: frozendict en Python 3.15

En Python 3.15 apareció el tipo integrado `frozendict` (PEP 814): no hace falta importar nada, y cualquier intento de modificarlo termina de inmediato con `TypeError`. Antes, un diccionario inmutable había que simularlo con un envoltorio del módulo `types` o con un paquete externo. Es justo lo que se necesita para la configuración común que ninguna función debería cambiar en silencio.

```
config = frozendict(importe_minimo=0, importe_maximo=500)
config["importe_maximo"] = 1000
# TypeError: 'frozendict' object does not support item assignment

estricto = config | {"importe_maximo": 300}   # nuevo frozendict, el original no cambia
cache = {config: "informe de mayo"}           # es hashable: sirve como clave
```

Lo que conviene saber, según el material del curso:

- `frozendict` hereda de `object`, no de `dict`, así que `isinstance(cfg, dict)` devuelve `False`. Para comprobar «cualquier diccionario» use `collections.abc.Mapping` o `isinstance(x, (dict, frozendict))`.
- Es hashable solo si lo son sus claves y valores: `frozendict(a=[1])` se crea, pero `hash()` falla. La inmutabilidad es superficial, como la de una tupla.
- El orden se conserva, pero `==` no lo tiene en cuenta.
- `json.dumps`, `pickle` y `copy` admiten frozendict.

## Errores típicos

- **KeyError** al leer: use `get` o una comprobación previa con `in`.
- **Una lista como clave**: conviértala en tupla, `d[tuple(lista)]`.
- **Modificar el diccionario dentro de un bucle**: recorra `list(d)`.
- **Esperar una copia al asignar**: use `copy()` o `deepcopy`.
- **`{}` en lugar de un conjunto vacío**: es un diccionario vacío.

## Resumen

El dict de Python es la estructura de datos principal del lenguaje: búsqueda rápida por clave, orden de inserción conservado, métodos prácticos como `get`, `setdefault` y `update`, recorrido con `items()`, ordenación con `sorted` y unión con el operador `|`. Tenga en cuenta las copias y las claves hashables, y para la configuración en Python 3.15 use `frozendict`. Los diccionarios, las listas, las tuplas y los conjuntos se practican en el curso «Python 3.15: de principiante a profesional» con un registro de gastos.

## FAQ

**¿Se pueden guardar en un diccionario valores de tipos distintos?**

Sí, los valores pueden ser de cualquier tipo y mezclarse: números, cadenas, listas, otros diccionarios. La única restricción es para las claves, que deben ser hashables.

**¿Cómo saber cuántos elementos tiene un diccionario?**

La función len(d) devuelve el número de pares clave — valor. Para un diccionario vacío devuelve 0, y en una condición if el diccionario vacío se considera falso.

**¿En qué se diferencia dict de defaultdict?**

defaultdict es un subtipo de dict que, al acceder a una clave inexistente, no lanza KeyError sino que crea el valor con una fábrica, por ejemplo int o list. En todo lo demás se comporta como un diccionario normal.

**¿Cómo obtener una clave a partir de un valor?**

No hay un método directo; hay que recorrer el diccionario: next((k for k, v in d.items() if v == valor), None). Si esta búsqueda se hace a menudo, conviene construir el diccionario inverso con {v: k for k, v in d.items()}.

**¿Cómo guardar un diccionario en un archivo?**

Lo habitual es el módulo json: json.dump(d, f, ensure_ascii=False, indent=2) escribe en un archivo abierto para escritura, y json.load(f) lo lee. Las claves se convierten en cadenas.


---
Source: Skillaria.Top (https://skillaria.top)
