Tuplas y conjuntos
Ya conoces las listas. Python tiene otros dos tipos de colección que resuelven problemas que las listas no pueden. Las tuplas guardan un grupo fijo de valores que nunca van a cambiar. Los conjuntos guardan solo valores únicos y te permiten comprobar si algo pertenece a la colección de forma instantánea, sin importar cuán grande sea.
Tuplas
Una tupla es un grupo ordenado de valores que no se puede cambiar después de crearlo. Los paréntesis definen una tupla, pero son opcionales. Lo que realmente la convierte en tupla es la coma. Una tupla de un solo elemento necesita una coma al final.
point = (10, 20)
rgb = (255, 128, 0)
dimensions = (1920, 1080)
single = (42,) # se requiere coma al final para una tupla de un solo elemento
also_tuple = 42, 99 # los paréntesis son opcionales; la coma es lo que crea la tuplaEl acceso por índice funciona exactamente igual que en una lista. Intentar cambiar un elemento lanza un TypeError:
point = (10, 20)
point[0] # 10
point[1] # 20
point[-1] # 20
point[0] = 99 # TypeError: 'tuple' object does not support item assignment(42,) necesita esa coma solitaria al final. Si intentas reasignar un elemento, obtienes un TypeError, y esa sensación de "esto está fijo" es justamente la razón para elegir una tupla. Cuándo usar una tupla
Usa una tupla cuando tienes un grupo pequeño de valores relacionados que van juntos y que no van a cambiar. Coordenadas (x, y), un color (r, g, b), un par nombre-puntaje ("Ana", 87). La estructura fija le indica a cualquiera que lea el código que ese grupo se trata como una sola unidad.
(r, g, b), un par nombre-puntaje. Como las tuplas son hashable, incluso puedes usar una como clave de dict. Una lista no puede hacer eso, y eso confunde a la gente la primera vez que lo intenta. locations = {}
locations[(40, -74)] = "Nueva York" # tupla como clave de dict, funciona
locations[[40, -74]] = "Nueva York" # lista como clave de dict, TypeErrorDesempaquetado
El desempaquetado extrae valores de una tupla y asigna cada uno a su propio nombre en una sola línea. La cantidad de nombres debe coincidir con la cantidad de valores. Usa * para capturar el resto de los elementos en una lista.
x, y = point. La cantidad de nombres tiene que coincidir con la cantidad de valores, a menos que agregues un *resto para recoger lo que quede. A mí me hizo clic el día que dejé de escribir point[0] y point[1] por todos lados. point = (10, 20)
x, y = point
print(x) # 10
print(y) # 20
first, *rest = [1, 2, 3, 4, 5]
# first = 1, rest = [2, 3, 4, 5]
head, *middle, tail = [1, 2, 3, 4, 5]
# head = 1, middle = [2, 3, 4], tail = 5Tuplas con nombre
Una tupla con nombre es una tupla donde cada posición tiene un nombre. En lugar de recordar que point[0] es la coordenada x, escribes point.x. Los valores siguen siendo inmutables; lo que ganas son nombres de atributo legibles en lugar de posiciones numéricas.
point.x en lugar de acordarte de que point[0] es la x. Sigue siendo completamente inmutable y se comporta como una tupla normal en todo lo demás. Tu yo del futuro, leyendo el código, te va a agradecer por los nombres. namedtuple vive en la biblioteca estándar, así que primero necesita un import: from collections import namedtuple. Los imports se explican a fondo en el capítulo de Módulos.
from collections import namedtuple
Point = namedtuple("Point", ["x", "y"])
Player = namedtuple("Player", ["name", "score", "level"])
p = Point(10, 20)
p.x # 10
p.y # 20
alice = Player("Ana", 87, 5)
alice.name # "Ana"
alice.score # 87Conjuntos
Un conjunto (set) es una colección de valores únicos sin un orden garantizado. Agregar el mismo valor dos veces no hace nada: un conjunto conserva solo una copia de cada elemento. Usa llaves para un conjunto con elementos, o set() para crear uno vacío.
set(), porque {} en secreto es un dict vacío. Ese último detalle atrapa a casi todo el mundo. tags = {"python", "principiante", "tutorial"}
numbers = {1, 2, 3, 4, 5}
empty = set() # NO {} (eso es un dict vacío)Agregar el mismo valor dos veces no cambia el conjunto:
tags.add("python") # tags no cambia, "python" ya estaba en el conjuntoCuándo usar un conjunto
Los conjuntos son la herramienta indicada para tres cosas: eliminar duplicados de una lista, comprobar rápidamente si algo está en una colección grande, y comparar dos grupos para ver qué comparten o qué los diferencia.
# Eliminar duplicados de una lista
raw = ["gato", "perro", "gato", "pájaro", "perro", "gato"]
unique = list(set(raw)) # ["gato", "perro", "pájaro"] (orden no garantizado)# Comprobación rápida de membresía
valid_codes = {"USD", "EUR", "GBP", "JPY"}
code = "EUR"
if code in valid_codes: # búsqueda instantánea, incluso con miles de códigos
print("Válido")Operaciones de conjuntos
Los conjuntos soportan las mismas operaciones que aprendiste en matemáticas: unión (todo lo que está en cualquiera de los dos conjuntos), intersección (solo lo que comparten ambos conjuntos) y diferencia (lo que tiene uno y el otro no). Python usa símbolos de operador para esto, y cada uno tiene un método equivalente.
| es unión (en cualquiera), & es intersección (en ambos), - es diferencia (en uno pero no en el otro), y ^ es diferencia simétrica (en uno pero no en ambos). Cada una también tiene un método con nombre completo como .union() si prefieres palabras a símbolos. a = {1, 2, 3, 4}
b = {3, 4, 5, 6}
a | b # {1, 2, 3, 4, 5, 6} (unión: todo lo que está en cualquiera)
a & b # {3, 4} (intersección: solo lo que está en ambos)
a - b # {1, 2} (diferencia: en a pero no en b)
b - a # {5, 6} (diferencia al revés)
a ^ b # {1, 2, 5, 6} (diferencia simétrica: en uno pero no en ambos)Estas también tienen forma de método: .union(), .intersection(), .difference(), .symmetric_difference().
Modificar conjuntos
Los conjuntos son mutables. .add() agrega un elemento. .update() agrega varios a la vez desde cualquier lista u otro iterable. .remove() elimina un elemento pero lanza un error si no está ahí. .discard() elimina en silencio si el elemento existe y no hace nada si no.
.add() mete un solo elemento, .update() agrega un lote entero desde una lista u otro iterable. El par que hay que tener claro es la eliminación: .remove() da error si el elemento no está ahí, mientras que .discard() se hace el desentendido y sigue adelante. Cuando no estés seguro de que algo está en el conjunto, .discard() te ahorra un error inesperado. tags = {"python", "principiante"}
tags.add("tutorial") # agregar un elemento
tags.update(["web", "api"]) # agregar varios elementos desde cualquier iterable
tags.remove("principiante") # eliminar, lanza KeyError si no se encuentra
tags.discard("faltante") # eliminar, sin error si no se encuentra
tags.pop() # eliminar y devolver un elemento arbitrario
tags.clear() # eliminar todoUsa .discard() cuando no estés seguro de si el elemento existe.
Frozen sets
Un frozen set es un conjunto que no puedes modificar después de crearlo. La razón principal para usar uno: los frozen sets son hashable, así que se pueden usar como claves de diccionario o guardarse dentro de otros conjuntos.
valid_statuses = frozenset({"active", "paused", "deleted"})
valid_statuses.add("archived") # AttributeError, frozenset es inmutableElegir la colección correcta
Cuatro tipos, cada uno con un rol claro. Pregúntate qué necesitas hacer con los datos y normalmente la elección correcta se vuelve obvia.
| list | tuple | set | dict | |
|---|---|---|---|---|
| Ordenado | Sí | Sí | No | Sí (orden de inserción) |
| Mutable | Sí | No | Sí | Sí |
| Duplicados | Sí | Sí | No | No (claves) |
| Acceso por | Índice | Índice | n/a | Clave |
| Úsalo cuando | Secuencia ordenada y modificable | Registro fijo | Valores únicos, membresía rápida | Búsqueda por clave-valor |
Una regla rápida de decisión:
- ¿Necesitas buscar algo por nombre? → dict
- ¿Necesitas una colección ordenada que vas a modificar? → list
- ¿Tienes un grupo fijo de valores relacionados? → tuple
- ¿Necesitas valores únicos o pruebas de membresía rápidas? → set
En la práctica
Usando tuplas para guardar registros fijos y un conjunto para rastrear valores únicos:
casa = (51.5074, -0.1278) # latitud, longitud
oficina = (51.5155, -0.0922)
casa_lat, casa_lon = casa
print(f"Casa: {casa_lat}, {casa_lon}")
# Rastrear visitantes únicos con un conjunto
visitantes = set()
visitantes.add("ana")
visitantes.add("beto")
visitantes.add("ana") # ya está en el conjunto, se ignora en silencio
visitantes.add("carla")
print(f"Visitantes únicos: {len(visitantes)}")
print(f"ana visitó: {'ana' in visitantes}")
print(f"dario visitó: {'dario' in visitantes}")
