set y frozenset: colecciones únicas con operaciones de conjuntos
Un set elimina duplicados automáticamente y soporta operaciones matemáticas de conjuntos: unión, intersección, diferencia. frozenset es su versión inmutable, que puede usarse como clave de dict.
Crear sets y propiedades fundamentales
Un set es una colección desordenada de elementos únicos. Se crea con llaves {} o con set(). A diferencia del dict, no tiene clave-valor — solo valores. Los duplicados se eliminan automáticamente.
# Crear un set con literales
colores = {"rojo", "verde", "azul", "rojo"} # "rojo" duplicado
print(colores) # {'rojo', 'verde', 'azul'} — un solo "rojo"
# Crear desde una lista (elimina duplicados)
numeros = set([1, 2, 2, 3, 3, 3, 4])
print(numeros) # {1, 2, 3, 4}
# set() sobre un string — letras únicas
letras = set("mississippi")
print(letras) # {'m', 'i', 's', 'p'}
# Propiedades clave
print(len(colores)) # 3
print("rojo" in colores) # True
print("negro" in colores) # False
# Añadir y eliminar elementos
colores.add("negro")
colores.discard("verde") # no lanza error si no existe
print(colores) # {'rojo', 'azul', 'negro'}{'rojo', 'verde', 'azul'}
{1, 2, 3, 4}
{'m', 'i', 's', 'p'}
3
True
False
{'rojo', 'azul', 'negro'}{} crea un dict vacío, no un set vacío. Para un set vacío usa siempre set().
Operaciones de conjuntos: |, &, -, ^
Los sets implementan todas las operaciones matemáticas de conjuntos con operadores simbólicos. Esto los hace perfectos para comparar colecciones y encontrar relaciones entre ellas.
python_devs = {"Ana", "Carlos", "Diana", "Eva"}
js_devs = {"Carlos", "Eva", "Felipe", "Gema"}
# Unión (|): todos los elementos de ambos sets
todos = python_devs | js_devs
print(todos) # {'Ana', 'Carlos', 'Diana', 'Eva', 'Felipe', 'Gema'}
# Intersección (&): solo los que están en ambos
ambos = python_devs & js_devs
print(ambos) # {'Carlos', 'Eva'}
# Diferencia (-): los de A que no están en B
solo_python = python_devs - js_devs
print(solo_python) # {'Ana', 'Diana'}
# Diferencia simétrica (^): los que están en uno u otro, pero no en ambos
solo_uno = python_devs ^ js_devs
print(solo_uno) # {'Ana', 'Diana', 'Felipe', 'Gema'}
# Versiones con métodos (equivalentes a los operadores)
print(python_devs.union(js_devs)) # igual que |
print(python_devs.intersection(js_devs)) # igual que &
print(python_devs.difference(js_devs)) # igual que -
# Subconjunto e superconjunto
a = {1, 2}
b = {1, 2, 3, 4}
print(a.issubset(b)) # True — a ⊆ b
print(b.issuperset(a)) # True — b ⊇ a
print(a.isdisjoint({5, 6})) # True — sin elementos comunes{'Ana', 'Carlos', 'Diana', 'Eva', 'Felipe', 'Gema'}
{'Carlos', 'Eva'}
{'Ana', 'Diana'}
{'Ana', 'Diana', 'Felipe', 'Gema'}
True
True
Truefrozenset como clave de dict
frozenset es la versión inmutable de set. No se puede modificar después de crearse, lo que lo hace hashable — puede usarse como clave de dict o como elemento de otro set.
# frozenset: inmutable y hashable
fs = frozenset([1, 2, 3])
print(fs) # frozenset({1, 2, 3})
print(type(fs)) # <class 'frozenset'>
# hash(frozenset) funciona — no así con set normal
print(hash(fs)) # algún entero
# Usar frozenset como clave de diccionario
# Caso real: agrupar combinaciones sin orden (A-B == B-A)
partidos = {
frozenset({"España", "Francia"}): 2,
frozenset({"Alemania", "Italia"}): 1,
frozenset({"España", "Alemania"}): 0,
}
# Buscar por cualquier orden de los dos equipos
clave = frozenset({"Francia", "España"})
print(partidos[clave]) # 2
# frozenset dentro de un set
grupos = {frozenset({"A", "B"}), frozenset({"C", "D"})}
print(grupos) # {frozenset({'A', 'B'}), frozenset({'C', 'D'})}
# Las operaciones de conjuntos funcionan igual
a = frozenset({1, 2, 3})
b = frozenset({2, 3, 4})
print(a & b) # frozenset({2, 3})frozenset({1, 2, 3})
<class 'frozenset'>
2
{frozenset({'A', 'B'}), frozenset({'C', 'D'})}
frozenset({2, 3})| set | frozenset |
|---|---|
| Mutable: add(), discard(), pop() | Inmutable: no cambia tras crearse |
| No hashable → no puede ser clave de dict | Hashable → puede ser clave de dict o elemento de set |
| Ideal para colecciones que cambian | Ideal para colecciones constantes y como clave |
Cuándo usar set vs lista
La elección entre set y lista afecta tanto la semántica como el rendimiento. Los sets tienen búsqueda O(1); las listas tienen búsqueda O(n).
import time
# Búsqueda en lista: O(n)
lista_grande = list(range(1_000_000))
start = time.perf_counter()
resultado = 999_999 in lista_grande
print(f"Lista: {time.perf_counter() - start:.4f}s") # ~0.01s
# Búsqueda en set: O(1)
set_grande = set(range(1_000_000))
start = time.perf_counter()
resultado = 999_999 in set_grande
print(f"Set: {time.perf_counter() - start:.6f}s") # ~0.000001s
# Caso 1: eliminar duplicados de una lista
emails_con_duplicados = ["ana@x.com", "carlos@x.com", "ana@x.com", "diana@x.com"]
emails_unicos = list(set(emails_con_duplicados))
# Caso 2: verificar membresía frecuente
palabras_permitidas = {"admin", "user", "guest", "moderator"}
rol = "admin"
if rol in palabras_permitidas: # O(1) — perfecto para esto
print("Acceso permitido")
# Cuando NO usar set: cuando el orden importa o necesitas duplicados
ranking = ["primer", "segundo", "segundo", "tercero"] # los duplicados son intencionales
# ranking como set perdería la posición y los empatesLista: 0.0089s
Set: 0.000001s
Acceso permitidoUsa set cuando necesites unicidad, operaciones de conjuntos o búsquedas rápidas de membresía. Usa lista cuando el orden importa, los duplicados son significativos, o necesitas acceso por índice.