Tuplas y conjuntos en Python
En esta lección
Además de las listas, Python tiene otras dos colecciones muy útiles: las tuplas, secuencias que no se pueden modificar, y los conjuntos, colecciones sin orden y sin elementos repetidos. En esta lección aprenderás a crearlas, para qué sirve cada una y por qué a veces son mejor opción que una lista. Al final tienes una tabla para elegir entre lista, tupla, conjunto y diccionario.
Tuplas: secuencias que no cambian
Una tupla se parece a una lista, pero se escribe con paréntesis y, una vez creada, no se puede modificar:
punto = (3, 5)
fecha = (2026, 9, 30)
alumno = ("Ana", 21, "DAW")
print(alumno[0]) # Ana
print(fecha[-1]) # 30
print(fecha[:2]) # (2026, 9)
print(len(alumno)) # 3
Los índices, el slicing, len, in, count e index funcionan igual que en las listas.
En realidad, lo que crea la tupla son las comas, no los paréntesis. Estos dos valores son iguales:
colores = ("rojo", "verde")
tambien = "rojo", "verde"
print(colores == tambien)
# True
La tupla de un solo elemento
Como mandan las comas, una tupla con un único elemento necesita una coma al final:
no_es_tupla = ("hola")
si_es_tupla = ("hola",)
print(type(no_es_tupla))
print(type(si_es_tupla))
# <class 'str'>
# <class 'tuple'>
Sin la coma, los paréntesis son solo paréntesis de agrupar, como en (2 + 3) * 4. La tupla vacía se escribe () o tuple().
También puedes convertir otra colección en tupla: tuple([1, 2, 3]) da (1, 2, 3).
Inmutabilidad: ¿para qué quiero algo que no cambia?
Si intentas modificar una tupla, obtienes un error:
fecha = (2026, 9, 30)
fecha[1] = 10
# TypeError: 'tuple' object does not support item assignment
Tampoco tiene append, remove ni sort. Puede parecer una limitación, pero tiene ventajas:
- Protege los datos: unas coordenadas o una fecha no deberían cambiar por accidente en mitad del programa.
- Expresa intención: quien lee
(40.4, -3.7)sabe que es un dato fijo con una estructura concreta. - Pueden ser claves de diccionario y elementos de un conjunto, cosa que una lista no puede.
- Son algo más ligeras y rápidas que las listas.
Una regla práctica: usa listas para colecciones de cosas del mismo tipo que crecen o cambian (los alumnos de una clase) y tuplas para registros con un número fijo de campos (un alumno: nombre, edad, curso).
Cuidado: la tupla es inmutable, pero si contiene una lista, esa lista sí se puede cambiar.
t = ([1, 2], 3)permitet[0].append(9). Lo que no puedes es sustituir el elementot[0]por otro.
Desempaquetado
Repartir los elementos de una tupla entre variables es la forma más natural de usarlas:
alumno = ("Ana", 21, "DAW")
nombre, edad, ciclo = alumno
print(f"{nombre} tiene {edad} años y estudia {ciclo}")
# Ana tiene 21 años y estudia DAW
El número de variables tiene que coincidir con el de elementos. Si alguno no te interesa, la convención es llamarlo _:
nombre, _, ciclo = alumno
El intercambio de variables a, b = b, a que viste en Variables funciona precisamente así: se crea una tupla a la derecha y se desempaqueta a la izquierda.
Y en los bucles, cada elemento de una lista de tuplas se desempaqueta directamente:
ventas = [("lunes", 120), ("martes", 95), ("miércoles", 140)]
for dia, importe in ventas:
print(f"{dia}: {importe} €")
# lunes: 120 €
# martes: 95 €
# miércoles: 140 €
Devolver varios valores desde una función
Cuando una función hace return a, b, en realidad devuelve una tupla:
def minimo_y_maximo(numeros):
return min(numeros), max(numeros)
resultado = minimo_y_maximo([4, 9, 1, 7])
print(resultado)
# (1, 9)
menor, mayor = minimo_y_maximo([4, 9, 1, 7])
print(menor, mayor)
# 1 9
Tuplas como claves de diccionario
Como son inmutables, las tuplas pueden ser claves de un diccionario. Muy útil para datos con dos coordenadas, como un tablero o un horario:
horario = {
("lunes", 9): "Programación",
("lunes", 10): "Bases de datos",
("martes", 9): "Sistemas",
}
print(horario[("lunes", 10)])
# Bases de datos
Con una lista como clave obtendrías TypeError: unhashable type: 'list'.
namedtuple: tuplas con nombres
Acceder a alumno[2] no dice mucho. Con namedtuple, del módulo collections, cada posición tiene además un nombre:
from collections import namedtuple
Producto = namedtuple("Producto", ["nombre", "precio", "stock"])
teclado = Producto("Teclado", 29.99, 12)
print(teclado.nombre) # Teclado
print(teclado.precio) # 29.99
print(teclado[2]) # 12 (sigue funcionando por índice)
print(teclado) # Producto(nombre='Teclado', precio=29.99, stock=12)
Sigue siendo inmutable. Cuando necesites objetos más completos, con métodos, lo harás con clases.
Conjuntos (set): sin duplicados y sin orden
Un conjunto es una colección que no admite elementos repetidos y no tiene orden. Se escribe con llaves:
etiquetas = {"python", "web", "python", "datos"}
print(etiquetas)
print(len(etiquetas))
# {'datos', 'web', 'python'} (el orden puede variar)
# 3
El "python" repetido desaparece solo. Y fíjate en el comentario: el orden en que se muestran los elementos no está garantizado. Por eso los conjuntos no tienen índices: etiquetas[0] da TypeError.
Crear un conjunto vacío
Aquí hay una trampa: {} no es un conjunto vacío, sino un diccionario vacío. Para un conjunto vacío se usa set():
vacio = set()
tampoco = {}
print(type(vacio))
print(type(tampoco))
# <class 'set'>
# <class 'dict'>
set() también convierte otras colecciones: set("banana") da un conjunto con las letras 'b', 'a' y 'n', en cualquier orden.
Añadir y quitar
asistentes = {"Ana", "Luis"}
asistentes.add("Eva") # añade un elemento
asistentes.add("Ana") # ya estaba: no pasa nada
asistentes.remove("Luis") # quita; da KeyError si no está
asistentes.discard("Pablo") # quita si está; si no, no hace nada
print(sorted(asistentes))
# ['Ana', 'Eva']
La diferencia entre remove y discard es solo qué pasa cuando el elemento no está. Usa discard si no te importa que no exista. También tienes pop() (quita uno cualquiera) y clear().
Como el orden de un conjunto es impredecible, en los ejemplos usamos sorted() para mostrar siempre el mismo resultado. sorted devuelve una lista ordenada.
Comprobar si algo está: muy rápido
La operación estrella de un conjunto es in. En una lista, Python tiene que mirar los elementos uno a uno; en un conjunto, la búsqueda es casi instantánea aunque tenga millones de elementos:
usuarios_bloqueados = {"spam99", "troll_01", "bot_x"}
usuario = "troll_01"
if usuario in usuarios_bloqueados:
print("Acceso denegado")
# Acceso denegado
Esto es posible porque los elementos de un conjunto deben ser inmutables (números, textos, tuplas). Una lista no puede estar dentro de un conjunto.
Quitar duplicados de una lista
El uso más frecuente de set en el día a día:
correos = ["ana@ej.es", "luis@ej.es", "ana@ej.es", "eva@ej.es", "luis@ej.es"]
unicos = set(correos)
print(len(correos), len(unicos))
# 5 3
Si necesitas conservar el orden original, el truco es list(dict.fromkeys(correos)), porque los diccionarios sí mantienen el orden de inserción:
print(list(dict.fromkeys(correos)))
# ['ana@ej.es', 'luis@ej.es', 'eva@ej.es']
Operaciones de conjuntos
Los conjuntos de Python funcionan como los de matemáticas. Imagina dos grupos de alumnos:
python = {"Ana", "Luis", "Eva", "Marta"}
java = {"Luis", "Marta", "Pablo"}
print(sorted(python | java)) # unión: en alguno de los dos
print(sorted(python & java)) # intersección: en los dos
print(sorted(python - java)) # diferencia: en python pero no en java
print(sorted(python ^ java)) # diferencia simétrica: en uno solo
# ['Ana', 'Eva', 'Luis', 'Marta', 'Pablo']
# ['Luis', 'Marta']
# ['Ana', 'Eva']
# ['Ana', 'Eva', 'Pablo']
| Operador | Método | Resultado |
|---|---|---|
a | b | a.union(b) | Elementos de a o de b |
a & b | a.intersection(b) | Elementos de a y de b |
a - b | a.difference(b) | Elementos de a que no están en b |
a ^ b | a.symmetric_difference(b) | Elementos que están solo en uno |
Los métodos tienen una ventaja: aceptan cualquier colección, no solo conjuntos: python.union(["Sara", "Iker"]).
Subconjuntos
requisitos = {"html", "css"}
sabe = {"html", "css", "javascript"}
print(requisitos <= sabe) # True: todos los requisitos están en sabe
print(requisitos.issubset(sabe)) # True: lo mismo con método
print(sabe.issuperset(requisitos)) # True
print(requisitos.isdisjoint({"sql"})) # True: no tienen nada en común
Un ejemplo real: comprobar qué requisitos le faltan a un candidato es simplemente una diferencia: requisitos - sabe.
frozenset: un conjunto inmutable
frozenset es a set lo que la tupla a la lista: la versión que no se puede modificar. No tiene add ni remove, pero puede ser clave de diccionario o elemento de otro conjunto:
vocales = frozenset("aeiou")
print("e" in vocales)
# True
Se usa poco, pero es útil para constantes: un conjunto de valores permitidos que nadie debería cambiar.
Errores frecuentes
- Olvidar la coma en la tupla de un elemento:
("hola")es unstr. Escribe("hola",). - Intentar modificar una tupla:
TypeError. Si necesitas cambiarla, conviértela en lista conlist(). - Crear un conjunto vacío con
{}: eso es un diccionario. Usaset(). - Acceder a un conjunto por índice:
conjunto[0]daTypeError. Conviértelo en lista o recórrelo confor. - Confiar en el orden de un conjunto: puede cambiar entre ejecuciones. Usa
sorted()si necesitas orden. - Meter listas en un conjunto o usarlas como clave:
TypeError: unhashable type: 'list'. Usa tuplas. removede un elemento que no está:KeyError. Usadiscard.
Resumen: ¿lista, tupla, conjunto o diccionario?
| Lista | Tupla | Conjunto | Diccionario | |
|---|---|---|---|---|
| Sintaxis | [1, 2] | (1, 2) | {1, 2} | {"a": 1} |
| Vacío | [] | () | set() | {} |
| Ordenada | Sí | Sí | No | Sí (orden de inserción) |
| Modificable | Sí | No | Sí | Sí |
| Duplicados | Sí | Sí | No | Claves no; valores sí |
| Acceso | Por índice | Por índice | Solo in y recorrer | Por clave |
| Úsala para | Colecciones que cambian | Registros fijos, varios valores de retorno | Quitar duplicados, pertenencia rápida, operaciones de conjuntos | Asociar claves con valores |
En la siguiente lección verás a fondo los diccionarios.
Pon a prueba lo que has aprendido
¿Te ha quedado claro? Márcala y verás tu progreso en el explorador.