2.1Listas, tuplas, diccionarios y conjuntos
Lo esencial en 30 segundos
- Cuatro contenedores, cuatro usos: lista ordenada modificable, tupla ordenada fija, dict por claves, set de valores únicos — el contenedor adecuado para cada uso.
- El diccionario es la estructura reina: lista de dicts = 90 % de los datos reales (API, CSV, JSON) — acceso por clave, .get() sin KeyError.
- Copiar no es asignar: = comparte, .copy() copia en superficie, deepcopy() lo copia todo — verificar con una prueba sobre lo anidado.
Minicaso hilado — Nabil y las ventas de Argel
Nabil, data analyst junior en Argel, recibe una exportación de ventas: una lista de líneas con duplicados, clientes que hay que encontrar por código, coordenadas GPS fijas y categorías que hay que deduplicar. Su primera versión busca los clientes recorriendo una lista — lento y frágil. Lo reconstruye: las ventas en lista de diccionarios (una clave por campo), el directorio de clientes en dict indexado por código con .get(), las coordenadas en tuplas inmutables, las categorías en set. Este mismo juego de datos vuelve en 2.2 (informes formateados con f-strings, guardado JSON), 2.3 (objetos Venta y Cliente) y 2.4 (archivo ausente gestionado correctamente).
Hilo conductor del nivel 2: este mismo juego de datos de Nabil vuelve en el curso, el esquema, el script y el desafío 48h — y continúa en 2.2, 2.3 y 2.4.
1. Cuatro contenedores, cuatro temperamentos
Las aplicaciones reales manipulan colecciones: una lista de ventas, un directorio de clientes, unas categorías. Python propone cuatro estructuras fundamentales. La lista ([]) es ordenada y modificable — la navaja suiza: añadir con append(), acceso por índice desde 0, último elemento en [-1]. La tupla (()) es ordenada pero inmutable: perfecta para datos fijos (coordenadas GPS, par fijo) y utilizable como clave de diccionario. El diccionario ({}) asocia claves con valores — la agenda: acceso por clave, no por posición. El conjunto (set()) solo conserva valores únicos, sin orden: ideal para deduplicar y comprobar la pertenencia con in. Nabil lo aplica: ventas en lista, coordenadas en tupla, clientes en dict, categorías en set.
2. El diccionario, estructura reina
El diccionario merece su estatus: cliente = {"nombre": "Nabil", "wilaya": "Argel"}, acceso cliente["nombre"], y sobre todo cliente.get("tel", "?") que devuelve un valor por defecto sin KeyError. Una lista de diccionarios representa el 90 % de los datos reales (líneas de API, de CSV, de JSON): cada línea es un dict, el conjunto es una lista. Se recorre con .keys() (claves), .values() (valores) y .items() (pares: for clave, valor in d.items()). Buscar por clave en un dict es directo; buscar por clave recorriendo una lista es lento y frágil — la regla de Nabil: en cuanto se busca por identificador, se indexa en dict.
3. Copiar sin compartir (el retorno de las etiquetas)
En Python, b = a no copia: los dos nombres apuntan al mismo objeto — modificar uno modifica el otro. b = a.copy() copia en superficie (los objetos anidados siguen compartidos), copy.deepcopy() copia todo, incluido lo anidado. Regla: copy() para una estructura plana, deepcopy() para lo anidado — y una pequeña prueba para verificar (id() o modificación de prueba). Nabil lo recuerda: su «copia» de la tabla mensual modificaba el original hasta que pasó a deepcopy para las líneas anidadas.
🔎 Ejemplo concreto — 🔎 Ejemplo concreto — Deduplicar y contar las wilayas de Nabil
El set deduplica, la comprensión cuenta, como en las ventas de Nabil:
ventas = ["Argel", "Orán", "Argel", "Blida", "Orán", "Argel"]
unicas = set(ventas)
conteos = {w: ventas.count(w) for w in unicas}
print(conteos) # {'Argel': 3, 'Orán': 2, 'Blida': 1}
Lectura: set() deduplica, la comprensión en dict cuenta cada wilaya.
Lección: tupla para las claves compuestas inmutables, dict para el acceso por clave.
Vocabulario de la sección
- Lista (list)
- Colección ordenada y modificable, entre corchetes: [1, 2, 3]. Añadido con append(), acceso por índice desde 0, [-1] para el último.
Ejemplo sencillo: Nabil ordena sus ventas en una lista recorrida con for.
Lo que no es: una estructura de acceso por clave.
Reflejo: orden para recorrer = lista.
- Tupla
- Colección ordenada pero INMUTABLE (no modificable), entre paréntesis: (lat, lon). Protege los datos fijos, utilizable como clave de dict.
Ejemplo sencillo: Las coordenadas GPS de Nabil son tuplas.
Lo que no es: una lista que se modifica a lo largo del programa.
Reflejo: dato fijo = tupla.
- Diccionario (dict)
- Conjunto de pares clave→valor, entre llaves: {"nombre": "Nabil"}. Acceso por clave, .get() con defecto sin KeyError, recorrido en .items().
Ejemplo sencillo: Nabil encuentra un cliente por su código vía directorio[codigo].
Lo que no es: una lista recorrida para buscar por identificador.
Reflejo: buscar por identificador = dict.
- Conjunto (set)
- Colección no ordenada de valores ÚNICOS: {1, 2, 3}. Ideal para deduplicar (set(lista)) y comprobar la pertenencia con in.
Ejemplo sencillo: Nabil deduplica las categorías con set(categorias).
Lo que no es: una lista con duplicados para comprobar la pertenencia.
Reflejo: únicos o pertenencia = set.
- Copia (asignación / copy / deepcopy)
- = comparte el mismo objeto; .copy() copia en superficie (anidado compartido); copy.deepcopy() copia todo, incluido lo anidado.
Ejemplo sencillo: Nabil duplica su tabla mensual anidada con deepcopy.
Lo que no es: una asignación creída copiada.
Reflejo: anidado para duplicar = deepcopy + prueba.
En la práctica — En práctica — Elegir y manipular la estructura adecuada
- Cree una lista de ventas, añada (append), retire (remove), ordene (sort) y acceda por índice. Recórrala con un bucle for.
- Cree un diccionario «cliente» (nombre, email, wilaya). Acceda por clave, añada una clave, use .get() con defecto, recorra con .items().
- Cree una tupla de coordenadas (inmutable) e intente modificarla: constate el error. Concluya sobre el interés de la inmutabilidad.
- Deduplique una lista con set() y luego cuente con una comprensión en dict. Verifique la diferencia entre = y .copy() sobre una lista.
Script listo para decir — justificar el dict ante un colega (30 s)
«Para las ventas de Nabil, conservo la lista para el orden, pero los clientes están indexados en dict por código: acceso directo, .get() con defecto en lugar de fallar, y recorrido en .items(). Recorrer la lista en cada búsqueda era lento y frágil — así es legible e instantáneo.»
Para decir mostrando una búsqueda por código antes (bucle) y después (dict).
Modelo copiar-pegar — directorio indexado y conteo
ventas = [
{"cliente": "C01", "wilaya": "Argel", "monto": 1200},
{"cliente": "C02", "wilaya": "Orán", "monto": 800},
{"cliente": "C01", "wilaya": "Argel", "monto": 450},
]
directorio = {"C01": {"nombre": "Nabil", "wilaya": "Argel"}, "C02": {"nombre": "Lina", "wilaya": "Orán"}}
for v in ventas:
cliente = directorio.get(v["cliente"], {"nombre": "desconocido"})
print(cliente["nombre"], "->", v["monto"])
wilayas = [v["wilaya"] for v in ventas]
print({w: wilayas.count(w) for w in set(wilayas)})
Exportación para deduplicar
Las ventas de Nabil llegan duplicadas: set() sobre los identificadores, y luego lista de dicts limpia.
Directorio por código
Cada cliente tiene un código único: dict indexado por código, .get() con defecto para los códigos desconocidos.
Coordenadas fijas
Los puntos de entrega son tuplas (lat, lon): inmutables, utilizables como claves de dict.
Tabla mensual para duplicar
El resumen anidado se duplica con deepcopy: el original de Argel queda intacto.
✓ Buenas prácticas
- Indexar en dict en cuanto se busque por identificador (nunca recorrer una lista).
- Usar .get() con defecto y .items() para recorrer correctamente.
- Copiar con conciencia: = comparte, copy() en superficie, deepcopy() para lo anidado.
✗ Errores frecuentes
- Buscar por clave recorriendo una lista (lento y frágil).
- Usar dict[clave] sin .get() y sufrir un KeyError.
- Creer que = copia la estructura (comparte el mismo objeto).
Puntos clave
- Lista = ordenada + modificable; tupla = ordenada + fija (inmutable).
- Diccionario = pares clave→valor (el directorio), omnipresente (JSON, config); .get() evita el KeyError.
- Conjunto = valores únicos, perfecto para deduplicar y comprobar con in.
- Los índices empiezan en 0; [-1] designa el último; = comparte, copy()/deepcopy() copian.
Desafío 48h — aplíquelo mañana
Tome una exportación real (la de Nabil o la suya): ordénela en lista de dicts, indexe un directorio en dict por código con .get(), deduplique una columna con set(), y demuestre la diferencia = frente a .copy() en un caso anidado.
- Puedo elegir la estructura adecuada (lista, tupla, dict, set) con un ejemplo de cada una.
- Sé indexar en dict, usar .get() y recorrer en .items().
- He verificado la diferencia entre asignación, copy() y deepcopy().
Preguntas frecuentes
¿Cuándo usar una tupla en lugar de una lista?
Use una tupla para datos que no deben cambiar (coordenadas GPS, fecha, un par fijo). La inmutabilidad protege contra modificaciones accidentales, permite usar la tupla como clave de diccionario y es ligeramente más rápida. Todo lo que evoluciona (añadidos, retiradas, ordenaciones) sigue siendo dominio de la lista.
¿Cómo recorrer un diccionario?
Use .items() para obtener los pares: «for clave, valor in mi_dict.items()». También puede recorrer solo las claves (.keys(), o directamente for clave in mi_dict) o los valores (.values()). Para un acceso seguro, prefiera .get(clave, defecto) a dict[clave] cuando la clave puede faltar.
¿Cuál es la diferencia entre una lista y una tupla?