📘 Contenido de la Lección 4.4 — Conjuntos (set
)
- Parte 1 — Introducción y creación de conjuntos
- Parte 2 — Eliminación de duplicados y operaciones básicas
- Parte 3 — Operaciones matemáticas: unión, intersección y diferencia
- Parte 4 — Métodos comunes (
add
,discard
,update
) y ejemplos prácticos - Parte 5 — Buenas prácticas, rendimiento y ejercicios finales
Haz clic en cada sección para navegar directamente.
Parte 1 — Introducción y creación de conjuntos (set
)
Objetivo: comprender qué es un conjunto en Python, sus propiedades (unicidad, no orden e inmutabilidad de elementos) y dominar su creación de forma correcta y segura.
1) ¿Qué es un conjunto (set
)?
Un conjunto es una colección de elementos únicos y no ordenados. Que sean únicos significa que un valor no puede repetirse; si lo intentas, Python lo deduplica automáticamente. Al ser no ordenados, no hay índice ni posición estable para acceder con [i]
.
# Unicidad: los duplicados desaparecen al crear/el conjunto
colores = {"rojo", "verde", "azul", "rojo"}
print(colores) # {'verde', 'azul', 'rojo'}
# No orden: el orden interno puede variar entre ejecuciones
numeros = {3, 1, 4, 1, 5, 9}
print(numeros) # p.ej. {1, 3, 4, 5, 9} (el orden no está garantizado)
Además, los elementos de un conjunto deben ser hashables (inmutables): números, cadenas o tuplas sí; listas o diccionarios, no. Intentar meter un elemento mutable lanza TypeError
.
# Válido: todos los elementos son hashables/inmutables
ok = {1, "hola", (1, 2)}
# Inválido: lista dentro del set (mutable) → TypeError
# fallo = {1, [2, 3]}
Colección | ¿Ordenada? | ¿Permite duplicados? | Mutabilidad del contenedor | Elementos requeridos |
---|---|---|---|---|
list | Sí | Sí | Mutable | Cualquiera |
tuple | Sí | Sí | Inmutable | Cualquiera |
set | No (sin índice) | No (únicos) | Mutable | Hashables |
2) Cómo crear conjuntos correctamente
Hay dos formas habituales de crear un set
en Python:
- Usando llaves
{}
con elementos literales. - Usando la función
set(iterable)
para construir desde otra colección.
# 1) Literal con llaves
colores = {"rojo", "verde", "azul", "rojo"} # 'rojo' repetido → se deduplica
print(colores) # {'verde', 'azul', 'rojo'}
# 2) A partir de un iterable (lista, tupla, etc.)
numeros = set([1, 2, 3, 2, 1])
print(numeros) # {1, 2, 3}
Ambos ejemplos demuestran la eliminación automática de duplicados al construir el conjunto.
{}
no crea un conjunto vacío, sino un diccionario vacío. Para un set vacío usa set()
.vacio_mal = {}
print(type(vacio_mal)) # <class 'dict'> ← Diccionario, no set
vacio_bien = set()
print(type(vacio_bien)) # <class 'set'> ← Conjunto vacío correcto
3) ¿Cuándo usar conjuntos?
- Eliminar duplicados de una lista rápidamente.
- Comprobar pertenencia (
x in set
) de forma muy eficiente incluso con muchos elementos. - Operaciones de teoría de conjuntos: unión, intersección, diferencia, etc. (las verás en Parte 3).
# Deduplicar rápido (sin mantener orden original)
datos = [3, 1, 4, 1, 5, 9, 2, 6, 5]
unicos = set(datos) # {1,2,3,4,5,6,9}
print(unicos)
# Pertenencia: O(1) promedio
frutas = {"manzana", "naranja", "plátano"}
print("manzana" in frutas) # True
print("uva" in frutas) # False
La comprobación de pertenencia en conjuntos es muy rápida gracias a su implementación con tablas hash.
4) Limitaciones y buenas prácticas desde el día 1
- No hay indexación: no puedes hacer
mi_set[0]
(usa bucles o convierte a lista si necesitas índices). - El orden no está garantizado: perfecto para colecciones, no para secuencias ordenadas.
- Solo elementos hashables: nada de listas/dicts dentro del set.
- Creación del set vacío: siempre
set()
, nunca{}
.
5) Mini-prácticas (check rápido)
- Crea un set a partir de
["a", "b", "a", "c", "c"]
e imprime su tamaño.letras = set(["a", "b", "a", "c", "c"]) print(len(letras)) # ¿Resultado?
- Intenta añadir una lista
[1,2]
al set{1, (2,3)}
. ¿Qué error obtienes? ¿Por qué?s = {1, (2, 3)} # s.add([1, 2]) # Descomenta y ejecuta → TypeError: unhashable type: 'list'
- Crea un set vacío de la forma correcta y verifica su tipo.
v = set() print(type(v))
6) Resumen ejecutivo
- set = colección de elementos únicos, no ordenada, sin índice.
- Creación: literal
{"a","b"}
o funciónset(iterable)
; el vacío esset()
, no{}
. - Elementos deben ser hashables (números,
str
, tuplas…). - Casos de uso: deduplicación, pertenencia rápida y operaciones de teoría de conjuntos.
Parte 2 — Eliminación de duplicados y operaciones básicas
Objetivo: dominar la deduplicación con set
y aplicar con solvencia las operaciones básicas: añadir, eliminar, consultar, iterar y convertir entre tipos.
1) Eliminar duplicados desde otras colecciones
La forma más directa de eliminar duplicados de una lista/tupla es construir un set
desde ese iterable. Recuerda: los conjuntos garantizan unicidad y no mantienen orden. Si después necesitas indexar, conviértelo de vuelta a lista.
# Deduplicación rápida (no preserva el orden original)
datos = [3, 1, 4, 1, 5, 9, 2, 6, 5]
unicos = set(datos) # {1, 2, 3, 4, 5, 6, 9}
print(unicos)
# Si necesitas índice, vuelve a lista (el orden resultante es arbitrario)
unicos_lista = list(unicos)
print(unicos_lista)
def dedupe_preservando_orden(seq):
return list(dict.fromkeys(seq))
print(dedupe_preservando_orden(datos)) # [3, 1, 4, 5, 9, 2, 6]
2) Añadir elementos: add()
y update()
add(x)
agrega un único elemento; update(iterable)
agrega varios (desde cualquier iterable o incluso varios iterables). Si el elemento ya existe, no pasa nada porque la colección es única.
tecnologias = {"Python", "JavaScript", "SQL"}
tecnologias.add("Java") # Un elemento
print(tecnologias) # {'Python', 'JavaScript', 'SQL', 'Java'}
nuevos = ["Go", "Rust", "TypeScript"]
tecnologias.update(nuevos) # Varios elementos
print(tecnologias) # {'Python', 'JavaScript', 'SQL', 'Java', 'Go', 'Rust', 'TypeScript'}
tecnologias.add("Python") # Ya existía → sin efecto
print(tecnologias)
Operación | Firma | Efecto | In-place |
---|---|---|---|
add | set.add(elem) | Inserta un único elemento si no estaba. | Sí |
update | set.update(*iterables) | Inserta varios elementos de uno o más iterables. | Sí |
3) Eliminar elementos: remove()
, discard()
, pop()
, clear()
Eliminar en sets es directo, con diferencias sutiles:
remove(x)
: borra x; si no existe, lanzaKeyError
.discard(x)
: borra x si existe; no lanza error si no está.pop()
: extrae y devuelve un elemento arbitrario.clear()
: deja el set vacío.
Todos operan in-place.
frutas = {"manzana", "naranja", "plátano"}
frutas.remove("naranja")
print(frutas) # {'manzana', 'plátano'}
frutas.discard("uva") # No existe → no rompe
print(frutas)
item = frutas.pop() # Quita un elemento cualquiera
print("Eliminado:", item, " → ahora:", frutas)
frutas.clear()
print(frutas) # set()
remove(x)
sin garantizar que x
exista. Mitigación: calcula if x in s: s.remove(x)
o usa directamente discard(x)
.4) Consultas básicas: len()
, pertenencia in
, copy()
Los sets dan consultas ágiles: len(s)
cuenta elementos; x in s
es muy eficiente por tabla hash; copy()
clona superficialmente el conjunto.
planetas = {"Mercurio", "Venus", "Tierra", "Marte"}
print(len(planetas)) # 4
print("Saturno" in planetas) # False
print("Tierra" in planetas) # True
copia = planetas.copy()
copia.add("Júpiter")
print(planetas) # {'Mercurio', 'Venus', 'Tierra', 'Marte'}
print(copia) # incluye 'Júpiter'
5) Iteración sobre conjuntos
Itera con for
como con cualquier colección. Recuerda: el orden de recorrido es arbitrario (no hay índice).
ciudades = {"Madrid", "Barcelona", "Valencia", "Sevilla"}
for c in ciudades:
print("Visitando", c)
6) Conversión entre tipos
Muy común pasar lista → set para deduplicar y luego set → lista para indexar.
lista = [1, 2, 2, 3, 4, 4, 5]
sin_dupes = set(lista) # {1, 2, 3, 4, 5}
lista_indexable = list(sin_dupes)
print(lista_indexable)
7) Relaciones rápidas: subconjunto y superconjunto
Para relaciones entre conjuntos: A.issubset(B)
(A ⊆ B) y A.issuperset(B)
(A ⊇ B). Ideales para validaciones y permisos.
pares = {2, 4, 6, 8}
todos = {1, 2, 3, 4, 5, 6, 7, 8}
print(pares.issubset(todos)) # True
frutas = {"manzana", "naranja", "plátano", "fresa", "kiwi"}
tropicales = {"plátano", "kiwi"}
print(frutas.issuperset(tropicales)) # True
8) Caso práctico exprés: asistencia a un evento
Aplica varias operaciones básicas en una mini-situación real.
# Registro de asistentes por día
dia1 = {"Ana", "Carlos", "Elena", "David", "Beatriz"}
dia2 = {"Carlos", "Elena", "Fernando", "Gabriela"}
# Alta de última hora
dia1.add("Héctor")
# Quien vino ambos días (intersección)
ambos = dia1.intersection(dia2)
# Cancelación (sin romper si no está)
dia1.discard("David")
# ¿Todos los del día 2 repiten con día 1?
todos_repiten = dia2.issubset(dia1)
# Total de únicos (unión)
total_unicos = len(dia1.union(dia2))
print("Día 1:", dia1)
print("Ambos días:", ambos)
print("¿Todos día2 en día1?:", todos_repiten)
print("Total únicos:", total_unicos)
9) Mini-retos (5′
- Dado
emails = ["a@x", "b@x", "a@x", "c@x", "b@x"]
, imprime cuántos emails únicos hay y una lista indexable con ellos.emails = ["a@x", "b@x", "a@x", "c@x", "b@x"] unicos = set(emails) print(len(unicos)) print(list(unicos))
- Implementa borrado seguro de un elemento
x
ens
sin lanzar error y registra si realmente se eliminó.def safe_discard(s, x): existed = x in s s.discard(x) return existed
- Crea una función que reciba una lista y devuelva otra lista sin duplicados y con el orden original.
def dedupe_stable(seq): return list(dict.fromkeys(seq))
10) Resumen ejecutivo
- Deduplicación:
set(iterable)
elimina duplicados; si necesitas índice, convierte alist
. - Añadir:
add()
(uno),update()
(varios). In-place. - Eliminar:
remove()
(error si no existe),discard()
(seguro),pop()
(arbitrario),clear()
(vacía). - Consultas:
len
, pertenenciain
(muy eficiente),copy()
. - Iteración: sin orden garantizado; no hay indexación.
Parte 3 — Operaciones matemáticas: unión, intersección y diferencia
Objetivo: dominar las operaciones de teoría de conjuntos en Python con sus dos estilos equivalentes: métodos y operadores, y saber cuándo usar cada uno.
0) Dos formas de decir lo mismo
Python ofrece métodos (union()
, intersection()
, difference()
) y operadores (|
, &
, -
) para expresar las mismas operaciones. Usa el que te resulte más legible: rendimiento equivalente.
Operación | Método | Operador | Resultado |
---|---|---|---|
Unión | A.union(B) | A | B | Todos los elementos de A y B (sin duplicados) |
Intersección | A.intersection(B) | A & B | Solo los elementos comunes |
Diferencia | A.difference(B) | A - B | Elementos de A que no están en B |
1) Unión: juntar sin duplicados
Equivalencias: A.union(B, C, ...)
⇄ A | B | C
. Devuelve un nuevo set.
eu = {"Madrid", "París", "Roma", "Berlín"}
asia = {"Tokio", "Pekín", "Seúl", "Bangkok"}
# Método
todas = eu.union(asia)
print(todas)
# Operador
todas2 = eu | asia
print(todas2) # Equivalente
A.union(B, C)
o A | B | C
.2) Intersección: lo que tienen en común
Equivalencias: A.intersection(B)
⇄ A & B
. Devuelve los elementos comunes.
mates = {"Ana", "Carlos", "Elena", "David"}
fisica = {"Carlos", "Elena", "Fernando", "Gabriela"}
# Método
ambas = mates.intersection(fisica)
print(ambas) # {'Carlos', 'Elena'}
# Operador
ambas2 = mates & fisica
print(ambas2) # Equivalente
Patrón real: “quién asistió ambos días”.
dia1 = {"Ana", "Carlos", "Elena", "David", "Beatriz"}
dia2 = {"Carlos", "Elena", "Fernando", "Gabriela"}
ambos_dias = dia1 & dia2
print(ambos_dias) # {'Carlos', 'Elena'}
3) Diferencia: lo que queda en A quitando B
Equivalencias: A.difference(B)
⇄ A - B
. Devuelve elementos exclusivos de A.
ingredientes = {"harina", "huevos", "azúcar", "leche", "mantequilla"}
usados = {"harina", "huevos", "azúcar"}
# Método
restantes = ingredientes.difference(usados)
print(restantes) # {'leche', 'mantequilla'}
# Operador
restantes2 = ingredientes - usados
print(restantes2) # Equivalente
Otro caso típico: “solo en A, no en B”.
grupo_a = {"Ana", "Carlos", "Elena", "David"}
grupo_b = {"Elena", "Fernando", "Gabriela", "Carlos"}
solo_a = grupo_a - grupo_b
print(solo_a) # {'Ana', 'David'}
4) Encadenar operaciones (combinaciones útiles)
Combina operadores como en álgebra de conjuntos: paréntesis para clarificar y listo. Equivalente a encadenar métodos.
A = {1, 2, 3, 4}
B = {3, 4, 5, 6}
C = {5, 6, 7, 8}
# Elementos en A y B, pero no en C
res = (A & B) - C
print(res) # {3, 4}
# Unión y diferencia en cadena (equivalente a métodos)
res2 = (A | C) - B
print(res2) # {1, 2, 7, 8}
union(t1, t2, t3)
).5) Mini-prácticas
- Dadas dos listas con duplicados, muestra la unión de emails únicos:
a = {"a@x", "b@x", "c@x"} b = {"b@x", "d@x"} print(a | b) # Unión print(a.union(b)) # Equivalente
- Dado el gusto de dos usuarios, muestra los géneros que comparten (intersección) y los exclusivos del primero (diferencia):
u1 = {"acción", "comedia", "ciencia ficción", "aventura"} u2 = {"drama", "comedia", "romance", "documental"} print(u1 & u2) # comunes print(u1 - u2) # solo u1
- Conjuntos A, B, C, calcula
(A & B) - C
usando métodos:res = A.intersection(B).difference(C) print(res)
6) Resumen ejecutivo
- Unión = todo sin duplicados (
union
⇄|
). - Intersección = solo lo común (
intersection
⇄&
). - Diferencia = lo que está en A y no en B (
difference
⇄-
). - Métodos vs operadores: mismos resultados y rendimiento; elige por legibilidad.
Parte 4 — Métodos comunes (add
, discard
, update
) y ejemplos prácticos
Objetivo: dominar la mutación segura de conjuntos con add
, discard
y update
, entendiendo sus matices, anti-patrones y usos reales.
1) Añadir elementos
add(x)
inserta un único elemento si no existía; update(iterable)
inserta varios elementos desde uno o más iterables. Si ya estaban, no pasa nada (la colección es única).
# add: un solo elemento (idempotente por unicidad)
tecs = {"Python", "JavaScript", "SQL"}
tecs.add("Java")
print(tecs) # {'Python', 'JavaScript', 'SQL', 'Java'}
# update: varios elementos desde cualquier iterable
nuevos = ["Go", "Rust", "TypeScript"]
tecs.update(nuevos)
print(tecs) # {'Python', 'JavaScript', 'SQL', 'Java', 'Go', 'Rust', 'TypeScript'}
# Intentar añadir un duplicado no cambia el conjunto
nums = {1, 2, 3}
nums.add(2)
print(nums) # {1, 2, 3}
Ref.: creación y update
con iterables; idempotencia por unicidad.
update()
acepta varios iterables: s.update(lista, otro_set, tupla)
.2) Eliminar elementos de forma segura
Para borrar un elemento concreto usa:
discard(x)
— no lanza error six
no está (seguro).remove(x)
— lanzaKeyError
six
no existe.
animales = {"perro", "gato", "conejo"}
# Borrado seguro (no rompe si no existe)
animales.discard("pájaro")
print(animales) # {'perro', 'gato', 'conejo'}
# Borrado estricto (rompe si no existe)
# animales.remove("pájaro") # KeyError
Ref.: diferencias remove vs discard.
Otros borrados útiles:
pop()
: extrae y devuelve un elemento arbitrario (no hay orden).clear()
: vacía el conjunto.
colores = {"rojo", "verde", "azul"}
quitado = colores.pop() # elemento cualquiera
print("Se eliminó:", quitado, "| ahora:", colores)
nums = {1, 2, 3, 4}
nums.clear()
print(nums) # set()
Ref.: pop
y clear
en operaciones básicas.
remove(x)
sin comprobar pertenencia — mejor discard(x)
o if x in s: s.remove(x)
.3) Actualizaciones masivas del propio conjunto
Cuando quieres modificar en sitio (no crear un nuevo set) con operaciones entre conjuntos, utiliza las versiones update:
intersection_update(S)
: deja solo elementos comunes conS
.difference_update(S)
: elimina del actual los elementos presentes enS
.symmetric_difference_update(S)
: deja los que están en uno u otro, pero no en ambos.update(S)
: añade los elementos deS
(equivalente a una unión in-place).
# intersection_update: filtra a los que cumplen dos condiciones
activos = {"user1", "user2", "user3", "user4"}
premium = {"user2", "user4", "user5"}
activos.intersection_update(premium)
print(activos) # {'user2', 'user4'}
# difference_update: quita los "ya tratados"
todos = {"Python", "Java", "SQL", "JavaScript", "C++"}
completados = {"Python", "SQL"}
todos.difference_update(completados)
print(todos) # {'Java', 'JavaScript', 'C++'}
# symmetric_difference_update: deja solo los no coincidentes
g1 = {"Ana", "Carlos", "David"}
g2 = {"Carlos", "Elena", "Fernando"}
g1.symmetric_difference_update(g2)
print(g1) # {'Ana', 'David', 'Elena', 'Fernando'}
# update: unión en sitio
frutas_locales = {"manzana", "pera", "naranja"}
frutas_importadas = {"piña", "mango", "kiwi"}
frutas_locales.update(frutas_importadas)
print(frutas_locales)
Ref.: familia update in-place para intersección, diferencia, diferencia simétrica y unión.
4) Ejemplos prácticos (listos para pegar)
4.1 Registro de asistencia (alta, baja, conteo únicos)
# Día 1 y Día 2
dia1 = {"Ana", "Carlos", "Elena", "David", "Beatriz"}
dia2 = {"Carlos", "Elena", "Fernando", "Gabriela"}
# Alta de última hora
dia1.add("Héctor")
# Quien vino ambos días
ambos = dia1.intersection(dia2)
# Borrado seguro (cancelación)
dia1.discard("David")
# ¿Todos los del día 2 están en el día 1?
todos_repiten = dia2.issubset(dia1)
# Total de asistentes únicos (unión)
total_unicos = len(dia1.union(dia2))
print("Día 1:", dia1)
print("Ambos días:", ambos)
print("¿Todos día2 en día1?:", todos_repiten)
print("Total únicos:", total_unicos)
Ref.: caso práctico de asistencia con add
, discard
, intersection
, issubset
y union
.
4.2 Inventario: altas, bajas “blandas” y sincronización
stock = {"laptop", "teléfono", "tablet", "auriculares"}
vendidos = {"laptop", "auriculares"}
# Quitar vendidos (sin crear un set nuevo)
stock.difference_update(vendidos)
print("Disponibles:", stock) # {'teléfono', 'tablet'}
# Llegan nuevos productos
nueva_llegada = {"monitor", "tablet"} # 'tablet' ya estaba
stock.update(nueva_llegada)
print("Tras llegada:", stock) # {'teléfono', 'tablet', 'monitor'}
# Baja blanda de un artículo que puede no existir
def baja_segura(s, item):
existed = item in s
s.discard(item)
return existed
print("Borrado 'mouse':", baja_segura(stock, "mouse")) # False, pero no rompe
Ref.: difference_update
y update
para sincronizar inventario; discard
para borrado tolerante.
4.3 Etiquetado de usuarios con filtros acumulativos (in-place)
usuarios = {"u1", "u2", "u3", "u4", "u5"}
activos = {"u1", "u2", "u4", "u6"}
premium = {"u2", "u5"}
# Qué usuarios quedan tras aplicar condición "activos"
usuarios.intersection_update(activos)
# Ahora, añade etiqueta "premium" (unión)
usuarios.update(premium)
print(usuarios) # {'u1', 'u2', 'u4', 'u5'}
Ref.: patrón de filtrado incremental con intersection_update
y enriquecimiento con update
.
5) Buenas prácticas rápidas
- Borrado seguro por defecto: prefiere
discard
aremove
salvo que quieras detectar la ausencia con excepción. - Mutaciones en lote: usa los sufijos
_update
(intersection_update
,difference_update
, etc.) para evitar crear objetos intermedios. - Coste de pertenencia:
x in s
es muy eficiente (tablas hash), ideal para validaciones y filtros. - Sin orden ni índice: si necesitas orden estable o posiciones, convierte a lista o usa otra estructura.
6) Micro-retos
- Implementa
agrega_unicos(s, *iterables)
que inserte elementos de múltiples iterables usandoupdate
.def agrega_unicos(s, *iterables): s.update(*iterables) return s
- Escribe
baja_segura(s, x)
que devuelvaTrue
si existía y se borró,False
si no.def baja_segura(s, x): existe = x in s s.discard(x) return existe
- Dado
pendientes
yhechos
(conjuntos), aplicadifference_update
y luego añadeextra
conupdate
.pendientes = {"tarea1", "tarea2", "tarea3"} hechos = {"tarea2"} extra = {"tarea4"} pendientes.difference_update(hechos) pendientes.update(extra) print(pendientes)
Parte 5 — Buenas prácticas, rendimiento y ejercicios finales
Objetivo: aplicar criterios profesionales al trabajar con set
, entender el rendimiento bajo el capó y validar el aprendizaje con ejercicios prácticos.
1) Buenas prácticas (do’s & don’ts)
1.1 Creación correcta y datos válidos
- Set vacío: usa
set()
, no{}
(eso crea un diccionario). - Elementos hashables únicamente (números,
str
, tuplas…). No metas listas ni diccionarios. - Deduplicación:
set(iterable)
elimina duplicados de forma directa y expresiva.
# Correctos
vacio = set()
ok = {1, "a", (2, 3)}
# Incorrectos (elementos mutables)
# mal = { [1,2], {"k":"v"} } # TypeError
1.2 Mutación segura y expresiva
- Inserciones:
add(x)
para uno;update(*iterables)
para “lotes”. - Borrado seguro: prioriza
discard(x)
frente aremove(x)
(evitasKeyError
). - In-place masivo: usa
intersection_update
,difference_update
,symmetric_difference_update
,update
para evitar objetos intermedios.
# Borrado tolerante a ausencia
def baja_segura(s, x):
existed = x in s
s.discard(x) # no lanza excepción
return existed
1.3 Operadores vs. métodos
- Mismo rendimiento; elige por legibilidad. Operadores para expresiones cortas; métodos cuando pasas múltiples conjuntos o quieres semántica explícita.
# Equivalentes
r1 = A.union(B).difference(C)
r2 = (A | B) - C
1.4 Orden, indexación y conversiones
- Sin orden ni índice: no uses
s[i]
. Para indexar u ordenar, convierte alist
. - Copias: usa
copy()
para clonar; la asignación simple referencia el mismo objeto.
# Indexar → primero convierto
s = {"a", "b", "c"}
lst = list(s) # orden arbitrario
2) Rendimiento: lo importante para producción
- Pertenencia (
x in s
) y muchas operaciones clave son muy eficientes gracias a tablas hash. - Intersección/Unión/Diferencia están optimizadas; trabaja in-place si procesas grandes volúmenes para reducir GC y picos de memoria.
- Operadores vs. métodos: equivalencia en rendimiento (los operadores llaman a los métodos). Decide por estilo/claridad.
# Micro-benchmark orientativo (idea de patrón, no cifras absolutas):
import time
A = set(range(100_000))
B = set(range(50_000, 150_000))
t0 = time.time()
_ = A & B # intersección (operador)
t1 = time.time()
_ = A.intersection(B) # intersección (método)
t2 = time.time()
print("Operador &:", (t1 - t0)*1000, "ms")
print("Método intersection:", (t2 - t1)*1000, "ms")
# En la práctica, tiempos similares.
set
ya te dan un baseline de rendimiento muy sólido.3) Checklist de calidad (rápido)
- ¿Usas
set()
para vacíos y deduplicación? ✔️ - ¿Evitas
remove
salvo que quieras capturar la ausencia? ✔️ - ¿Aplicas
_update
para mutaciones masivas? ✔️ - ¿Conviertes a lista antes de indexar/ordenar? ✔️
- ¿Eliges operadores o métodos por legibilidad/contexto? ✔️
4) Ejercicios finales
4.1 Deduplicación con preservación de orden
Enunciado: Implementa dedupe_stable(seq)
que devuelva una lista sin duplicados manteniendo el orden de aparición.
def dedupe_stable(seq):
return list(dict.fromkeys(seq)) # patrón clásico
Tip: set
elimina duplicados pero no preserva orden; usa dict.fromkeys
si el orden importa.
4.2 Normalización de inventario
Enunciado: Dado stock
y vendidos
(sets), elimina vendidos in-place y añade nueva_llegada
sin duplicados.
def normaliza(stock, vendidos, nueva_llegada):
stock.difference_update(vendidos)
stock.update(nueva_llegada)
return stock
Aplica difference_update
y update
para eficiencia y claridad.
4.3 Filtrado incremental de usuarios
Enunciado: Parte de usuarios
, deja solo los activos
e incorpora premium
(unión) sin crear sets temporales.
def filtra_y_enriquece(usuarios, activos, premium):
usuarios.intersection_update(activos)
usuarios.update(premium)
return usuarios
Intersección y unión in-place para pipelines sencillos.
4.4 Auditoría de permisos
Enunciado: Comprueba si permisos_requeridos ⊆ permisos_usuario
y devuelve faltantes.
def faltantes(permisos_usuario, permisos_requeridos):
if permisos_requeridos.issubset(permisos_usuario):
return set()
return permisos_requeridos - permisos_usuario
Subconjuntos y diferencia: validación típica de scopes/roles.
4.5 Preferencias de contenidos (expresión compuesta)
Enunciado: Dados A, B, C (sets), calcula “lo que está en A ∩ B y no en C” de dos formas equivalentes.
def expr(A, B, C):
via_metodos = A.intersection(B).difference(C)
via_operadores = (A & B) - C
return via_metodos, via_operadores
Equivalencia operadores↔métodos en resultados y rendimiento.
5) Soluciones (resumen)
- 4.1
dict.fromkeys
para orden estable;set
si solo importa unicidad. - 4.2
difference_update
+update
(mutación en sitio, menos memoria). - 4.3
intersection_update
+update
(pipeline declarativo). - 4.4
issubset
/-
para auditorías de permisos. - 4.5
intersection
/difference
⇄&
/-
(equivalentes).