Contenedores asociativos en Python — ejercicios con duraciones, géneros musicales y un recomendador de canciones (y las llaves vacías que no eran un conjunto)

Quería contar cuántas canciones distintas había escuchado en un día. Lo tenía clarísimo: un conjunto, porque los conjuntos no guardan repetidos. Empecé con las llaves vacías, vistas = {}, recorrí el historial haciendo vistas.add(cancion) y Python me contestó: AttributeError: 'dict' object has no attribute 'add'.

«¿Dict? Si yo he hecho un conjunto.» No. Las llaves vacías {} crean un diccionario vacío, no un conjunto, y los diccionarios no tienen add. Para un conjunto vacío hay que escribir set(). Es uno de esos fallos que te comes una vez y ya no se te olvida nunca, y es el primer fragmento de «¿Qué falla aquí?».

Hoy practicamos con tres ejercicios de dificultad creciente, tres fragmentos para depurar y un chuletario para llevarte. Estos ejercicios usan la teoría de contenedores asociativos y repasan lo de la práctica. Puedes hacerlos sin ellas, pero te costará más.

Cómo usar este artículo

  1. Lee el enunciado de un nivel y no mires la solución todavía.
  2. Escribe tu versión, ejecútala y compara tu salida con la esperada.
  3. Si te atascas, mira solo la 🔵 pista y vuelve a intentarlo.
  4. Cuando termines, compara con la solución y fíjate en por qué está hecha así, no solo en que dé la misma salida.

Ningún ejercicio usa input(): los datos van fijos en el código. Cuando muestres un conjunto, hazlo con sorted(…), porque un set no tiene un orden fijo y tu salida podría no coincidir con la esperada.

Nivel 1 · Básico: las duraciones

Enunciado. Parte del diccionario duraciones = {"Luz de neón": 215, "Café a las 3": 187} (título → segundos) y haz, en este orden:

  1. Añade "Pixel" con 176 segundos.
  2. Cambia la duración de "Café a las 3" a 190.
  3. Quita "Luz de neón" guardando su duración en una variable. Muestra el diccionario y esa variable.
  4. Muestra la duración de "Marzo", que no está, sin que salte ningún error: tiene que salir 0.
  5. Muestra si "Pixel" y "Marzo" están en el diccionario.
  6. Recorre el diccionario en orden alfabético mostrando cada canción en formato minutos:segundos (con dos cifras en los segundos) y, al final, el total de segundos.

Salida esperada:

{'Café a las 3': 190, 'Pixel': 176} 215
0
True False
Café a las 3: 3:10
Pixel: 2:56
Total: 366 segundos

Para quitar una pareja y quedarte con su valor, pop. Para leer sin error, get con un valor por defecto. Para recorrer en orden, sorted(duraciones) te da las claves ordenadas. Y para los segundos con dos cifras, el formato :02d.

Nivel 2 · Intermedio: géneros y artistas

Enunciado. Tienes el catálogo como una lista de tuplas (título, artista, género):

CANCIONES = [
    ("Luz de neón", "Marea Alta", "pop"),
    ("Café a las 3", "Los Turnos", "indie"),
    ("Domingo lento", "Noa Vela", "indie"),
    ("Kilómetro 0", "Marea Alta", "pop"),
    ("Pixel", "Bit Bang", "electrónica"),
    ("Volver a casa", "Lía Sur", "indie"),
    ("Bajo cero", "Bit Bang", "pop"),
]

Escribe tres funciones:

  • contar_generos(canciones): diccionario género → número de canciones.
  • artistas_por_genero(canciones): diccionario género → conjunto de artistas de ese género.
  • mas_variado(artistas): recibe el diccionario anterior y devuelve el género con más artistas distintos (en caso de empate, el primero que aparezca).

Muestra el recuento, los artistas de cada género (géneros en orden alfabético y artistas ordenados), el género más variado y los artistas que tienen canciones a la vez en pop y en electrónica.

Salida esperada:

{'pop': 3, 'indie': 3, 'electrónica': 1}
electrónica: ['Bit Bang']
indie: ['Los Turnos', 'Lía Sur', 'Noa Vela']
pop: ['Bit Bang', 'Marea Alta']
Género con más artistas: indie
En pop y en electrónica: ['Bit Bang']

contar_generos es el contador de siempre. En artistas_por_genero, crea el conjunto vacío del género la primera vez que aparezca (con set()) y luego haz add. Pop e indie tienen 3 canciones cada uno, pero no 3 artistas distintos: ahí está la gracia de usar conjuntos. Y para «a la vez en pop y en electrónica», piensa en una operación de conjuntos.

Nivel 3 · Desafío: un recomendador de canciones

Enunciado. Cada usuario tiene un conjunto con sus canciones guardadas:

GUARDADAS = {
    "ana": {"Luz de neón", "Café a las 3", "Pixel", "Domingo lento"},
    "luis": {"Pixel", "Café a las 3", "Volver a casa"},
    "sara": {"Domingo lento", "Luz de neón", "Café a las 3", "Bajo cero"},
    "pablo": {"Marzo"},
}

Escribe tres funciones:

  • afinidad(a, b): la parte común de dos conjuntos, de 0 a 1: len(a & b) / len(a | b). Si los dos están vacíos, 0.
  • mas_parecido(usuario, guardadas): el otro usuario con más afinidad. Si nadie comparte nada con él, None.
  • recomendar(usuario, guardadas): una tupla con el usuario más parecido y la lista ordenada de sus canciones que usuario todavía no tiene. Si no hay nadie parecido, (None, []).

Recorre todos los usuarios mostrando su recomendación (o sin recomendaciones) y, al final, la afinidad entre Ana y Sara en porcentaje.

Salida esperada:

ana: se parece a sara -> ['Bajo cero']
luis: se parece a ana -> ['Domingo lento', 'Luz de neón']
sara: se parece a ana -> ['Pixel']
pablo: sin recomendaciones
Afinidad ana-sara: 60%

En mas_parecido, recorre guardadas.items() y salta al propio usuario con continue. Empieza con la mejor afinidad en 0 y compara con > estricto: así, alguien con afinidad 0 nunca gana y Pablo se queda sin recomendaciones. Lo que recomiendas es una diferencia de conjuntos.

¿Qué falla aquí?

Tres fragmentos con un fallo cada uno. Antes de leer la explicación, haz dos cosas: predice si da error y, si no lo da, qué imprime.

Fragmento 1

vistas.py

historial = ["Pixel", "Marzo", "Pixel"]
vistas = {}
for cancion in historial:
    vistas.add(cancion)
print(len(vistas))

*Piénsalo antes de seguir: queremos contar las canciones distintas. ¿Qué imprime python3 vistas.py?*

Explicación. Es el fallo del principio:

Traceback (most recent call last):
  File "vistas.py", line 4, in <module>
    vistas.add(cancion)
    ^^^^^^^^^^
AttributeError: 'dict' object has no attribute 'add'

{} crea un diccionario vacío. El mensaje lo dice claramente: el objeto es un 'dict', y los diccionarios no tienen método add (en un diccionario se añade con vistas[clave] = valor). Las llaves vacías son del diccionario porque llegó antes al lenguaje que los conjuntos.

Crear un conjunto vacío con {}. Con elementos, {"Pixel", "Marzo"} sí es un conjunto, porque no lleva dos puntos; vacío, no.

Arreglo. Un conjunto vacío se crea con set():

historial = ["Pixel", "Marzo", "Pixel"]
vistas = set()                  # conjunto vacío de verdad
for cancion in historial:
    vistas.add(cancion)         # la segunda "Pixel" no se añade otra vez
print(len(vistas))
2

Si alguna vez dudas de qué tienes entre manos, print(type(vistas)) te lo dice al momento.

Fragmento 2

favoritas.py

valoraciones = {}
valoraciones[["Pixel", "Bit Bang"]] = 5
print(valoraciones)

¿Da error? ¿Qué imprime? Queremos valorar una canción identificándola por título y artista.

Explicación. Sí hay error:

Traceback (most recent call last):
  File "favoritas.py", line 2, in <module>
    valoraciones[["Pixel", "Bit Bang"]] = 5
    ~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^
TypeError: unhashable type: 'list'

Unhashable significa que ese objeto no puede ser clave. Las claves de un diccionario tienen que ser inmutables, y una lista puede cambiar después de usarla como clave (con un append, por ejemplo). Si eso pasara, Python ya no sabría dónde la guardó, así que directamente no lo permite.

Usar una lista como clave de un diccionario o como elemento de un conjunto. Pasa mucho cuando la clave tiene que ser «compuesta», como título y artista.

Arreglo. Una tupla guarda lo mismo y sí es inmutable:

valoraciones = {}
valoraciones[("Pixel", "Bit Bang")] = 5      # una tupla sí puede ser clave
print(valoraciones)
print(valoraciones[("Pixel", "Bit Bang")])
{('Pixel', 'Bit Bang'): 5}
5

Para leer el valor tienes que usar la misma tupla como clave. valoraciones[("Bit Bang", "Pixel")] sería otra clave distinta, porque en las tuplas el orden importa.

Fragmento 3

buscar.py

canciones = {"c01": "Luz de neón", "c02": "Café a las 3"}
if "Café a las 3" in canciones:
    print("La tengo")
else:
    print("No la tengo")

¿Da error? ¿Qué imprime? Queremos saber si tenemos la canción «Café a las 3».

Explicación. No da error, pero la respuesta es incorrecta:

No la tengo

La canción está, pero como valor. in sobre un diccionario solo mira las claves, que aquí son los códigos "c01" y "c02". Para Python, la pregunta era «¿hay alguna clave que se llame Café a las 3?», y la respuesta correcta es que no.

El operador in en un diccionarioUn diccionario con las claves c01 y c02 y los valores Luz de neón y Café a las 3. in sin más solo busca en la columna de las claves; para buscar en los valores hay que usar .values().cancionesclavesvalores"c01""Luz de neón""c02""Café a las 3""c02" in cancionesTrue"Café a las 3" in cancionesFalse"Café a las 3" in canciones.values()True
in mira solo la columna de las claves. Para preguntar por un valor, díselo con .values().

Usar in con un diccionario para buscar un valor. No da error, así que el fallo pasa desapercibido hasta que la respuesta no cuadra.

Arreglo. Pregunta explícitamente por los valores:

canciones = {"c01": "Luz de neón", "c02": "Café a las 3"}
if "Café a las 3" in canciones.values():     # buscar entre los VALORES
    print("La tengo")
else:
    print("No la tengo")
La tengo

Buscar con in entre las claves es instantáneo; buscar entre los valores obliga a Python a recorrerlos todos. Si vas a preguntar muchas veces por títulos, quizá el título debería ser la clave.

Soluciones

Mira esta sección solo cuando hayas intentado el ejercicio. Si tu código es distinto pero da la misma salida, también es una solución válida.

Solución del Nivel 1

duraciones.py

duraciones = {"Luz de neón": 215, "Café a las 3": 187}

duraciones["Pixel"] = 176                  # añadir una pareja nueva
duraciones["Café a las 3"] = 190           # cambiar el valor de una clave que ya existe
quitada = duraciones.pop("Luz de neón")    # quitarla y quedarnos con su valor
print(duraciones, quitada)

print(duraciones.get("Marzo", 0))          # clave que no está: 0, sin error
print("Pixel" in duraciones, "Marzo" in duraciones)

total = 0
for titulo in sorted(duraciones):          # las claves, en orden alfabético
    segundos = duraciones[titulo]          # tenemos la clave: consulta directa
    total += segundos
    print(f"{titulo}: {segundos // 60}:{segundos % 60:02d}")
print("Total:", total, "segundos")

Por qué está hecho así:

  • Las líneas 3 y 4 tienen exactamente la misma forma, duraciones[clave] = valor, pero hacen cosas distintas: la primera añade (Pixel no existía) y la segunda sustituye (Café a las 3 sí).
  • pop quita la pareja y devuelve el valor, así que quitada vale 215. Con del la pareja también desaparecería, pero perderíamos el valor.
  • get("Marzo", 0) devuelve el valor por defecto porque la clave no existe. Con duraciones["Marzo"] habría saltado un KeyError.
  • En el bucle, sorted(duraciones) da una lista con las claves ordenadas, y como tenemos cada clave, leemos su valor directamente con corchetes. No hace falta get: sabemos que la clave existe, porque la acabamos de sacar del propio diccionario.
  • 190 segundos son 190 // 60 = 3 minutos y 190 % 60 = 10 segundos: 3:10. El total es 190 + 176 = 366.

Solución del Nivel 2

generos.py

CANCIONES = [
    ("Luz de neón", "Marea Alta", "pop"),
    ("Café a las 3", "Los Turnos", "indie"),
    ("Domingo lento", "Noa Vela", "indie"),
    ("Kilómetro 0", "Marea Alta", "pop"),
    ("Pixel", "Bit Bang", "electrónica"),
    ("Volver a casa", "Lía Sur", "indie"),
    ("Bajo cero", "Bit Bang", "pop"),
]


def contar_generos(canciones):
    """Diccionario género -> número de canciones."""
    cuentas = {}
    for titulo, artista, genero in canciones:
        cuentas[genero] = cuentas.get(genero, 0) + 1      # el contador de siempre
    return cuentas


def artistas_por_genero(canciones):
    """Diccionario género -> conjunto de artistas (sin repetir)."""
    resultado = {}
    for titulo, artista, genero in canciones:
        if genero not in resultado:            # primera vez que sale el género
            resultado[genero] = set()          # su conjunto vacío
        resultado[genero].add(artista)         # el set ya se encarga de no repetir
    return resultado


def mas_variado(artistas):
    """Género con más artistas distintos."""
    mejor = None
    for genero, conjunto in artistas.items():
        if mejor is None or len(conjunto) > len(artistas[mejor]):
            mejor = genero
    return mejor


print(contar_generos(CANCIONES))
artistas = artistas_por_genero(CANCIONES)
for genero in sorted(artistas):
    print(f"{genero}: {sorted(artistas[genero])}")
print("Género con más artistas:", mas_variado(artistas))
print("En pop y en electrónica:", sorted(artistas["pop"] & artistas["electrónica"]))

Por qué está hecho así:

  • El for de las dos primeras funciones desempaqueta cada tupla en tres variables, aunque cada función use solo algunas. Si escribieras dos variables, el desempaquetado fallaría.
  • contar_generos es el contador con get(genero, 0) + 1. Pop e indie salen con 3 canciones.
  • artistas_por_genero construye un diccionario de conjuntos. Sigue los mismos dos pasos que el diccionario de listas de la práctica (asegurar que existe el conjunto y luego añadir), pero ahora no hace falta comprobar repetidos: Marea Alta sale dos veces en pop y el add repetido no hace nada.
  • Por eso pop, con 3 canciones, solo tiene 2 artistas (Marea Alta y Bit Bang), mientras que indie tiene 3. El género más variado es indie, aunque los dos tengan las mismas canciones.
  • mas_variado es el patrón del máximo a mano, pero comparando tamaños de conjuntos: len(conjunto) > len(artistas[mejor]). Guarda solo el nombre del género y consulta su conjunto cuando lo necesita.
  • La última línea es una intersección: Bit Bang tiene una canción pop (Bajo cero) y una electrónica (Pixel).

¿te has fijado en que, en indie, Lía Sur sale después de Los Turnos? sorted compara los strings carácter a carácter según su código Unicode, y la í con tilde va después de todas las letras sin tilde, incluida la o. Por eso «Lí…» queda detrás de «Lo…». Para un orden alfabético «de diccionario» hay herramientas específicas, pero para este tema nos basta con saber por qué pasa.

Solución del Nivel 3

recomendador.py

GUARDADAS = {
    "ana": {"Luz de neón", "Café a las 3", "Pixel", "Domingo lento"},
    "luis": {"Pixel", "Café a las 3", "Volver a casa"},
    "sara": {"Domingo lento", "Luz de neón", "Café a las 3", "Bajo cero"},
    "pablo": {"Marzo"},
}


def afinidad(a, b):
    """Parte común de dos conjuntos, de 0 a 1 (0 si los dos están vacíos)."""
    union = a | b
    if not union:                        # un conjunto vacío es "falso"
        return 0
    return len(a & b) / len(union)


def mas_parecido(usuario, guardadas):
    """Otro usuario con más afinidad, o None si nadie comparte nada."""
    mejor, mejor_afinidad = None, 0
    for otro, canciones in guardadas.items():
        if otro == usuario:              # no se compara consigo mismo
            continue
        valor = afinidad(guardadas[usuario], canciones)
        if valor > mejor_afinidad:       # con > estricto, afinidad 0 nunca gana
            mejor, mejor_afinidad = otro, valor
    return mejor


def recomendar(usuario, guardadas):
    """(usuario más parecido, canciones suyas que este no tiene, ordenadas)."""
    otro = mas_parecido(usuario, guardadas)
    if otro is None:
        return None, []
    return otro, sorted(guardadas[otro] - guardadas[usuario])


for usuario in GUARDADAS:
    otro, lista = recomendar(usuario, GUARDADAS)
    if otro is None:
        print(f"{usuario}: sin recomendaciones")
    else:
        print(f"{usuario}: se parece a {otro} -> {lista}")
print(f"Afinidad ana-sara: {afinidad(GUARDADAS['ana'], GUARDADAS['sara']):.0%}")

Este es el mapa de afinidades que calcula el programa. Cada fila es un usuario, y en coral está la columna que gana para esa fila:

Afinidad entre cada pareja de usuariosTabla de doble entrada con la afinidad entre ana, luis, sara y pablo. La más alta es ana con sara, 60 %. Pablo tiene 0 % con todos.analuissarapabloana—40%60%0%luis40%—17%0%sara60%17%—0%pablo0%0%0%—
Cada celda es len(a & b) / len(a | b). Para cada fila, la recomendación sale de la columna con el porcentaje más alto (marcado en coral).

Por qué está hecho así:

  • afinidad calcula primero la unión y comprueba si está vacía con if not union:. Un conjunto vacío cuenta como falso, así que la condición se cumple solo cuando los dos conjuntos están vacíos. Sin esa comprobación, dividiríamos entre 0.
  • Ana y Sara comparten 3 canciones (Luz de neón, Café a las 3 y Domingo lento) y entre las dos tienen 5 distintas: 3 / 5 = 0,6, el 60%.
  • mas_parecido usa dos variables a la vez, mejor y mejor_afinidad, que se asignan juntas con una asignación múltiple. El continue salta al propio usuario: si no, todos se parecerían más a sí mismos (afinidad 1).
  • Empezar mejor_afinidad en 0 y comparar con > hace que una afinidad de 0 nunca gane. Pablo no comparte nada con nadie, así que mejor se queda en None.
  • Fíjate en la fila de Luis en el dibujo: su afinidad con Ana es 40% y con Sara, 17%. Gana Ana, y la recomendación es lo que tiene Ana y Luis no: ANA - LUIS, ordenado.
  • recomendar devuelve siempre una tupla de dos elementos, también cuando no hay nadie parecido (None, []). Así, el bucle de abajo puede desempaquetar siempre en otro, lista sin preocuparse de qué caso es.
  • El bucle final recorre GUARDADAS directamente, es decir, sus claves: los nombres de los usuarios.

Míralo paso a paso en Python Tutor

El fallo del fragmento 3 se entiende mucho mejor en vivo. Python Tutor ejecuta el código línea a línea y dibuja el diccionario como una tabla con las claves a la izquierda y los valores a la derecha. Elige Python 3 y pega este programa:

canciones = {"c01": "Luz de neón", "c02": "Café a las 3"}
print("c02" in canciones)
print("Café a las 3" in canciones)
print("Café a las 3" in canciones.values())
for codigo in canciones:
    print(codigo, canciones[codigo])

Salida esperada:

True
False
True
c01 Luz de neón
c02 Café a las 3

Qué observar, paso a paso:

  1. Nada más crear canciones, Python Tutor dibuja dos filas. Fíjate en qué hay a la izquierda ("c01", "c02"): esas son las claves, lo único que mira in.
  2. La primera comprobación busca "c02" en esa columna de la izquierda y lo encuentra: True.
  3. La segunda busca "Café a las 3" en la misma columna. Aunque lo ves en la tabla, está en la columna de la derecha: False.
  4. La tercera usa .values(), que es justo la columna de la derecha: ahora sí, True.
  5. En el bucle final, mira cómo codigo va tomando las claves una a una y cómo canciones[codigo] salta a la fila correspondiente para sacar el título.

Chuletario: Contenedores asociativos

Lo esencial en 30 segundos

  • Diccionario: parejas clave: valor. Se busca por clave, y buscar por clave es instantáneo.
  • **d[k] = v añade si la clave es nueva y sustituye si ya existía. Leer** una clave que no existe da KeyError.
  • **in y for miran las claves.** Para valores, .values(); para parejas, .items().
  • **get(k, defecto)** lee sin miedo. El contador es d[k] = d.get(k, 0) + 1.
  • Las claves deben ser inmutables: strings, números, tuplas. Nunca listas.
  • Conjunto: elementos únicos y sin orden. Vacío con set(), nunca con {}.
  • Álgebra: | unión, & intersección, - diferencia, ^ simétrica.

Sintaxis de diccionarios

Quiero…CódigoResultado
Crear vacíod = {} o dict(){}
Crear con datos{"Pixel": 176, "Marzo": 198}Diccionario con 2 parejas
Desde parejasdict([("pop", 3), ("indie", 2)]){'pop': 3, 'indie': 2}
Añadir o cambiard["Pixel"] = 180Crea o sustituye
Leer (seguro que existe)d["Pixel"]El valor o KeyError
Leer sin errord.get("Pixel", 0)El valor o 0
¿Existe la clave?"Pixel" in dTrue / False
¿Existe el valor?176 in d.values()True / False
Quitar y quedarse el valord.pop("Pixel")El valor quitado
Quitar sin másdel d["Pixel"]—
Número de parejaslen(d)Entero
Recorrer clavesfor k in d:Claves en orden de inserción
Recorrer parejasfor k, v in d.items():Tuplas desempaquetadas
Claves ordenadassorted(d)Lista
Suma de los valoressum(d.values())Número
Contadord[k] = d.get(k, 0) + 1Suma 1 a la clave
Anidadod["c02"]["titulo"]Un corchete por nivel

Sintaxis de conjuntos

Quiero…CódigoResultado
Crear vacíoset()set()
Crear con datos{"pop", "indie"}Conjunto de 2 elementos
Quitar repetidos de una listaset(lista)Conjunto
Añadir unos.add("rock")Si ya estaba, no cambia
Añadir varioss.update(["jazz", "funk"])Añade cada elemento
Quitar (debe estar)s.remove("rock")KeyError si no está
Quitar (si está)s.discard("rock")Nunca da error
Unióna | bLo de alguno de los dos
Interseccióna & bLo de los dos
Diferenciaa - bLo de a que no está en b
Diferencia simétricaa ^ bLo que no comparten
Subconjuntoa <= b / a < bTrue / False
Mostrar en orden fijosorted(s)Lista ordenada

Plantillas de los ejercicios

Contador (Niveles 1 y 2):

cuentas = {}
for elemento in datos:
    cuentas[elemento] = cuentas.get(elemento, 0) + 1

Diccionario de conjuntos (o de listas) (Nivel 2):

resultado = {}
for titulo, artista, genero in canciones:
    if genero not in resultado:
        resultado[genero] = set()          # o [] si quieres una lista
    resultado[genero].add(artista)         # o .append(…) con listas

Máximo a mano sobre un diccionario (Nivel 2):

mejor = None
for clave, valor in d.items():
    if mejor is None or valor > d[mejor]:
        mejor = clave

Afinidad y recomendación con conjuntos (Nivel 3):

def afinidad(a, b):
    union = a | b
    if not union:
        return 0
    return len(a & b) / len(union)

recomendadas = sorted(guardadas[otro] - guardadas[usuario])

Errores que más se repiten

SíntomaCausa probable
KeyError: 'Pixel'Lees una clave que no existe: usa in o get
KeyError en un contador con +=d[k] += 1 necesita leer antes: usa d[k] = d.get(k, 0) + 1
KeyError al hacer appendLa clave aún no tiene su lista: créala antes (if k not in d: d[k] = [])
AttributeError: 'dict' object has no attribute 'add'Creaste el conjunto vacío con {}: usa set()
TypeError: unhashable type: 'list'Usas una lista como clave o como elemento de un conjunto: usa una tupla
in dice False y el dato estáLo buscas entre las claves y es un valor: usa .values()
RuntimeError: dictionary changed size during iterationAñades o borras claves mientras recorres: recorre list(d)
ValueError: too many values to unpackRecorres el diccionario sin .items() y desempaquetas en dos variables
El conjunto sale en otro orden cada vezLos set no tienen orden: muéstralo con sorted
TypeError: unsupported operand type(s) for +: 'set' and 'set'La unión se escribe con |, no con +

Antes de entregar, pregúntate

  1. ¿Puede faltar alguna de las claves que leo? Si puede, ¿la protejo con in o con get?
  2. ¿Busco por la clave (directo) o entre los valores (hay que recorrer)? ¿Estoy usando in en el sitio correcto?
  3. ¿Mis conjuntos vacíos están creados con set() y mis claves son inmutables?
  4. ¿Muestro los conjuntos con sorted para que la salida sea siempre la misma?

Si quieres repasar el tema desde la base, vuelve a la teoría de contenedores asociativos.

Sigue aprendiendoTodos los artículos de Python

¿Te ha servido este artículo? Así puedes apoyar SergioLearns →

Sigue aprendiendo

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *