Listas, tuplas, diccionarios y conjuntos

Listas, tuplas, diccionarios y conjuntos resuelven casi todo en Python. Esta lección va de cuándo usar cada uno, de la diferencia entre d["x"] y d.get("x") —que causa la mitad de los errores— y de una copia que no copia.

Cuatro estructuras, y cuándo cada una

Ordenada Mutable Repetidos Se escribe
list sí sí sí [1, 2, 3]
tuple sí no sí (1, 2, 3)
dict sí (por inserción) sí claves no {"a": 1}
set no sí no {1, 2, 3}

Listas

compra = ["pan", "uva", "pan"]
compra.append("kiwi")
compra.insert(0, "sal")
compra[1:3]      # rebanada
compra[::-1]     # al revés
compra.remove("pan")
compra.pop()
salida['sal', 'pan', 'uva', 'pan', 'kiwi']  len=5
compra[0]=sal  compra[-1]=kiwi  compra[1:3]=['pan', 'uva']
compra[::-1] = ['kiwi', 'pan', 'uva', 'pan', 'sal']
tras remove('pan'): ['sal', 'uva', 'pan', 'kiwi']   (solo quita la primera)
pop() devuelve 'kiwi' y deja ['sal', 'uva', 'pan']

Dos cosas que conviene interiorizar pronto. Los índices negativos cuentan desde el final: [-1] es el último, sin necesidad de calcular la longitud. Y las rebanadas [inicio:fin] incluyen el inicio y excluyen el fin, igual que range; por eso [1:3] da dos elementos.

remove quita solo la primera aparición, y si no está lanza ValueError.

Tuplas

punto = (3, 4)
x, y = punto          # desempaquetado
punto[0] = 9
salidadesempaquetado: x=3 y=4
TypeError: 'tuple' object does not support item assignment

Una tupla es una lista que no se puede cambiar. Eso parece una limitación y es justo lo que la hace útil: como no cambia, se puede usar de clave en un diccionario o de elemento en un conjunto, cosa que con una lista es imposible.

Y el desempaquetado —x, y = punto— está por todas partes en Python: es lo que permite que una función devuelva varias cosas, o que un for sobre un diccionario te dé la clave y el valor a la vez.

Diccionarios

stock = {"pan": 3, "uva": 10}
stock["pan"] = 5                          # sustituye
stock.get("kiwi")                         # None, no revienta
stock.get("kiwi", 0)                      # 0
stock.setdefault("kiwi", 1)
stock["pan"] = stock.get("pan", 0) + 2
for k, v in stock.items(): ...
salida{'pan': 5, 'uva': 10}
get('kiwi')         None
get('kiwi', 0)      0
setdefault('kiwi')  1  -> {'pan': 5, 'uva': 10, 'kiwi': 1}
sumar 2 a pan:      7
acceso directo: KeyError 'fantasma'
La diferencia entre d["x"] y d.get("x") es la fuente número uno de KeyError. Los corchetes revientan si la clave no está; get devuelve None, o lo que le digas como segundo argumento. Usa corchetes cuando la clave tiene que estar —así el error te avisa de un fallo real— y get cuando puede faltar.

Desde Python 3.7 los diccionarios conservan el orden de inserción. Antes no, y aún hay tutoriales que dicen lo contrario.

Conjuntos

a, b = {1,2,3}, {3,4,5}
a | b    # unión
a & b    # intersección
a - b    # diferencia
list(set([1,1,2,3,3]))
salidaunion {1, 2, 3, 4, 5}  intersección {3}  diferencia {1, 2}
quitar repetidos de [1,1,2,3,3]: [1, 2, 3]
pertenencia en set es instantánea: True

Dos usos que justifican conocerlos: quitar repetidos en una línea, y preguntar si algo está. Ese in sobre un conjunto es instantáneo pase lo que pase, mientras que sobre una lista recorre elemento a elemento. Con mil elementos no se nota; con un millón, sí.

Ojo: {} vacío es un diccionario, no un conjunto. El conjunto vacío es set().

La copia que no copia

original = [[1,2],[3,4]]
superficial = original.copy()
superficial[0].append(99)
salidaoriginal tras tocar la copia: [[1, 2, 99], [3, 4]]

copy() hace una copia superficial: la lista de fuera es nueva, pero las de dentro son las mismas. Tocar la copia cambia el original. Para que no pase:

import copy
profunda = copy.deepcopy(original)

Y esto no es una curiosidad de las listas: es la misma idea de siempre, que una variable guarda una referencia al objeto, no el objeto. Vale igual para los diccionarios anidados.

Quieres… Usa
Una secuencia que crece list
Algo que no debe cambiar, o una clave tuple
Buscar por clave dict, y get si puede faltar
Quitar repetidos o preguntar si está set
Copiar algo anidado copy.deepcopy
El último elemento x[-1]

Todo el código de esta lección se ejecutó con Python 3.14 en un contenedor limpio antes de publicarla; las salidas y los errores están copiados de esa ejecución.