Contenedores asociativos en Python — práctica con un contador de reproducciones, gustos que se cruzan y una biblioteca de playlists (y el bucle que se quejó de que el diccionario cambiaba)

Después de arreglar mi contador de reproducciones con get, quise hacer limpieza: borrar del diccionario las canciones que se habían quedado con 0 reproducciones porque había puesto su cuenta a cero a mano. Me pareció lo más natural del mundo:

for cancion in cuentas:
    if cuentas[cancion] == 0:
        del cuentas[cancion]

Y Python respondió con esto:

RuntimeError: dictionary changed size during iteration

«¿Cómo que cambió de tamaño? Si es lo que le he pedido.» Precisamente por eso. El for va recorriendo las claves del diccionario una a una, y yo le estaba quitando claves mientras las recorría. Python no sabe por dónde seguir y prefiere pararse antes que saltarse canciones o leer una que ya no existe. La solución es recorrer una copia de las claves, for cancion in list(cuentas):, o, todavía mejor, construir un diccionario nuevo solo con las que quieres conservar.

Hoy practicamos los contenedores asociativos con tres programas completos de una app de música: un contador de reproducciones con varias preguntas sobre el historial, una comparación de gustos entre dos amigos con álgebra de conjuntos y una biblioteca con usuarios, playlists y canciones guardadas en diccionarios anidados. Esta práctica se apoya en la teoría de contenedores asociativos. Si todavía dudas con las listas dentro de otras estructuras, repasa antes mutabilidad, copias y matrices: aquí aparecen en casi cada línea.

Cómo usar este artículo

Cada programa está completo en un solo archivo. Cópialo, guárdalo con el nombre que se indica y lánzalo desde la terminal con python3 nombre.py. Debajo de cada programa tienes la salida real, un dibujo o una tabla con lo que pasa por dentro y una explicación por partes. Antes de mirar la salida, intenta predecirla: con los diccionarios, saber qué clave existe en cada momento es exactamente lo que tienes que entrenar.

Ningún programa usa input(). Los datos van fijos en el código para que tu salida sea idéntica a la que ves aquí. Y los conjuntos se muestran siempre con sorted, porque un set no tiene un orden fijo y podría salirte en otro orden.

Programa 1 · El contador de reproducciones

El historial es una lista de tuplas (canción, artista), en el orden en que se escucharon. Queremos responder tres preguntas: cuántas veces ha sonado cada canción, cuál es la más escuchada y qué canciones tiene cada artista.

contador.py

HISTORIAL = [
    ("Luz de neón", "Marea Alta"),
    ("Café a las 3", "Los Turnos"),
    ("Luz de neón", "Marea Alta"),
    ("Kilómetro 0", "Marea Alta"),
    ("Domingo lento", "Noa Vela"),
    ("Luz de neón", "Marea Alta"),
    ("Café a las 3", "Los Turnos"),
]


def contar(historial):
    """Devuelve un diccionario canción -> veces que se ha escuchado."""
    cuentas = {}                                   # empieza vacío
    for cancion, artista in historial:             # cada tupla se desempaqueta en dos
        # get(cancion, 0): las veces que lleva, o 0 si es la primera vez que sale
        cuentas[cancion] = cuentas.get(cancion, 0) + 1
    return cuentas


def mas_escuchada(cuentas):
    """Devuelve la pareja (canción, veces) con más reproducciones."""
    mejor = None                                   # todavía no hemos visto ninguna
    for cancion, veces in cuentas.items():
        # mejor[1] son las veces de la mejor hasta ahora
        if mejor is None or veces > mejor[1]:
            mejor = (cancion, veces)
    return mejor


def por_artista(historial):
    """Devuelve un diccionario artista -> lista de sus canciones, sin repetir."""
    resultado = {}
    for cancion, artista in historial:
        if artista not in resultado:               # primera vez que aparece el artista:
            resultado[artista] = []                # le creamos su lista vacía
        if cancion not in resultado[artista]:      # así la canción no se repite
            resultado[artista].append(cancion)
    return resultado


cuentas = contar(HISTORIAL)
print(cuentas)
print("La más escuchada:", mas_escuchada(cuentas))
for artista, canciones in por_artista(HISTORIAL).items():
    print(f"{artista}: {', '.join(canciones)}")

Lánzalo con python3 contador.py:

{'Luz de neón': 3, 'Café a las 3': 2, 'Kilómetro 0': 1, 'Domingo lento': 1}
La más escuchada: ('Luz de neón', 3)
Marea Alta: Luz de neón, Kilómetro 0
Los Turnos: Café a las 3
Noa Vela: Domingo lento

La función contar, vuelta a vuelta

contar es el patrón del contador de la teoría, aplicado a un historial de verdad. Lo único nuevo es que cada elemento es una tupla y el for la desempaqueta en cancion y artista (el artista no se usa aquí, pero hay que recogerlo igualmente para que el desempaquetado cuadre).

Cómo crece el diccionario cuentasTabla de siete vueltas del bucle. En cada una se lee una canción, get devuelve la cuenta que llevaba (0 la primera vez) y el diccionario cuentas suma 1 a esa canción.vueltacanción leídaget dacuentas después de la vuelta1Luz de neón0Luz de neón 12Café a las 30Luz de neón 1Café a las 3 13Luz de neón1Luz de neón 2Café a las 3 14Kilómetro 00Luz de neón 2Café a las 3 1Kilómetro 0 15Domingo lento0Luz de neón 2Café a las 3 1Kilómetro 0 1Domingo lento 16Luz de neón2Luz de neón 3Café a las 3 1Kilómetro 0 1Domingo lento 17Café a las 31Luz de neón 3Café a las 3 2Kilómetro 0 1Domingo lento 1
Cada vuelta suma 1 a una sola clave. Cuando la canción es nueva, get devuelve 0 y la clave aparece por primera vez (en coral).

Fíjate en dos cosas del dibujo:

  • La primera vez que sale una canción, get devuelve 0 y la línea cuentas[cancion] = 0 + 1 crea la clave. Por eso el diccionario crece en las vueltas 1, 2, 4 y 5.
  • Las demás veces, get devuelve lo que llevaba y la misma línea sustituye el valor. En las vueltas 3, 6 y 7 el diccionario no crece: solo cambia un número.
  • El orden de las claves en el resultado es el orden en que aparecieron por primera vez: Luz de neón, Café a las 3, Kilómetro 0, Domingo lento.

La función mas_escuchada: el máximo a mano

def mas_escuchada(cuentas):
    """Devuelve la pareja (canción, veces) con más reproducciones."""
    mejor = None                                   # todavía no hemos visto ninguna
    for cancion, veces in cuentas.items():
        # mejor[1] son las veces de la mejor hasta ahora
        if mejor is None or veces > mejor[1]:
            mejor = (cancion, veces)
    return mejor

¿Por qué no usar directamente max(cuentas.values())? Porque eso te da el 3, pero no te dice de qué canción es. Necesitamos recorrer las parejas con items() y guardar la mejor pareja que hayamos visto:

  • mejor empieza en None, que significa «todavía no he visto ninguna».
  • La condición mejor is None or veces > mejor[1] tiene dos partes. La primera solo es verdad en la primera vuelta y hace que la primera canción sea la mejor provisional. La segunda compara con las veces de la mejor (mejor[1], el segundo elemento de la tupla).
  • El orden de las dos partes importa: si mejor es None, el or ya es verdadero y Python no evalúa mejor[1], que daría error.
  • Con > estricto, en caso de empate se queda la que apareció primero.
Vueltacancion, veces¿Gana?mejor después
1'Luz de neón', 3Sí (mejor era None)('Luz de neón', 3)
2'Café a las 3', 2No (2 > 3 es falso)('Luz de neón', 3)
3'Kilómetro 0', 1No('Luz de neón', 3)
4'Domingo lento', 1No('Luz de neón', 3)

Cuando ya domines esto, existe una forma corta: max(cuentas.items(), key=lambda pareja: pareja[1]). Hace lo mismo que el bucle, pero merece la pena escribir el bucle al menos una vez para entender qué hace por dentro.

La función por_artista: un diccionario de listas

def por_artista(historial):
    """Devuelve un diccionario artista -> lista de sus canciones, sin repetir."""
    resultado = {}
    for cancion, artista in historial:
        if artista not in resultado:               # primera vez que aparece el artista:
            resultado[artista] = []                # le creamos su lista vacía
        if cancion not in resultado[artista]:      # así la canción no se repite
            resultado[artista].append(cancion)
    return resultado

Aquí el valor de cada clave es una lista que va creciendo. El error que todo el mundo comete la primera vez es escribir directamente resultado[artista].append(cancion): en cuanto aparece un artista nuevo, esa línea intenta leer resultado[artista] para hacer el append, y salta el KeyError. Por eso hay dos if:

  1. El primero se asegura de que el artista tiene su lista. Si es la primera vez que aparece, se la crea vacía.
  2. El segundo evita repetir canciones: Luz de neón sale tres veces en el historial, pero en la lista de Marea Alta solo aparece una.

Al final, ', '.join(canciones) junta la lista de títulos en un solo string separado por comas, que es lo que se imprime.

Escribir if artista not in resultado: resultado[artista] = [cancion] y luego, en un else, el append. Funciona, pero si añades después el if de las repetidas tendrás que copiarlo en las dos ramas. Separar «asegurar que existe la lista» de «añadir a la lista» deja el código más limpio.

Pruébalo tú

  • Añade al historial ("Pixel", "Bit Bang") cuatro veces. ¿Qué devuelve ahora mas_escuchada? Predícelo y compruébalo.
  • Cambia > por >= en mas_escuchada y empata Café a las 3 con Luz de neón (añade una escucha más de Café). ¿Cuál gana con cada operador, y por qué?
  • Escribe segundos_por_artista(historial, duraciones) que devuelva un diccionario artista → segundos totales escuchados, usando el patrón del contador con get(artista, 0) + duracion.

Programa 2 · ¿Qué tenemos en común Ana y yo? Gustos con conjuntos

Ana y Luis han guardado cada uno sus canciones. Como cada canción solo puede estar guardada una vez, lo natural es usar conjuntos, y entonces todas las preguntas interesantes son una sola operación.

gustos.py

ANA = {"Luz de neón", "Café a las 3", "Pixel", "Domingo lento", "Kilómetro 0"}
LUIS = {"Pixel", "Café a las 3", "Volver a casa", "Kilómetro 0", "Bajo cero"}
CATALOGO = ANA | LUIS | {"Marzo", "Sin cobertura"}   # todo lo que hay en la app


def mostrar(titulo, canciones):
    """Muestra un conjunto ordenado (un set no tiene un orden fijo)."""
    print(f"{titulo} ({len(canciones)}): {', '.join(sorted(canciones))}")


mostrar("En común", ANA & LUIS)            # intersección
mostrar("Entre los dos", ANA | LUIS)       # unión
mostrar("Solo Ana", ANA - LUIS)            # diferencia
mostrar("Solo Luis", LUIS - ANA)           # diferencia al revés
mostrar("No comparten", ANA ^ LUIS)        # diferencia simétrica

# Afinidad: qué parte de todo lo que escuchan entre los dos es común (de 0 a 1)
afinidad = len(ANA & LUIS) / len(ANA | LUIS)
print(f"Afinidad: {afinidad:.0%}")

# Lo que hay en la app y no ha guardado ninguno de los dos
mostrar("Sin oyentes", CATALOGO - (ANA | LUIS))

print(ANA <= CATALOGO, (ANA & LUIS) < ANA)   # inclusión

Lánzalo con python3 gustos.py:

En común (3): Café a las 3, Kilómetro 0, Pixel
Entre los dos (7): Bajo cero, Café a las 3, Domingo lento, Kilómetro 0, Luz de neón, Pixel, Volver a casa
Solo Ana (2): Domingo lento, Luz de neón
Solo Luis (2): Bajo cero, Volver a casa
No comparten (4): Bajo cero, Domingo lento, Luz de neón, Volver a casa
Afinidad: 43%
Sin oyentes (2): Marzo, Sin cobertura
True True

Antes de leer la explicación, mira el dibujo: cada zona es el resultado de una de las líneas del programa.

Las canciones de Ana y Luis en un diagrama de VennDos círculos, Ana y Luis, dentro del rectángulo del catálogo. Solo de Ana: Domingo lento y Luz de neón. De los dos: Café a las 3, Kilómetro 0 y Pixel. Solo de Luis: Bajo cero y Volver a casa. Fuera de los dos: Marzo y Sin cobertura.CATALOGOANALUISDomingo lentoLuz de neónCafé a las 3Kilómetro 0PixelBajo ceroVolver a casaMarzo, Sin coberturaANA – LUISANA & LUISLUIS – ANACATALOGO – (ANA | LUIS)
Cada zona del dibujo es una operación del programa: las coloreadas suman la unión, y la de en medio es la intersección.

Entre líneas:

  • CATALOGO se construye con una unión de tres conjuntos: las de Ana, las de Luis y dos que no ha guardado nadie. Así nos aseguramos de que todo lo guardado está en el catálogo.
  • mostrar recibe un título y un conjunto. Usa len para el número entre paréntesis y sorted para imprimir siempre en el mismo orden. Fíjate en que sorted devuelve una lista, y por eso join puede juntarla.
  • En común (&): las 3 de la zona central del dibujo.
  • Entre los dos (|): 7 canciones distintas. Ana tiene 5 y Luis tiene 5, pero 5 + 5 = 10 cuenta dos veces las 3 comunes: 10 − 3 = 7.
  • Solo Ana y Solo Luis (-): las zonas de los lados. Cambiar el orden de los operandos cambia el resultado.
  • No comparten (^): las 4 de los dos lados juntos. Comprueba la cuenta: 7 entre los dos = 3 en común + 4 que no comparten.
  • Afinidad: 3 en común entre 7 en total da 0,4285…, y el formato :.0% lo multiplica por 100, lo redondea sin decimales y le pone el símbolo: 43%.
  • Sin oyentes: el paréntesis es importante. CATALOGO - (ANA | LUIS) primero junta lo de los dos y luego lo quita del catálogo.
  • Inclusión: ANA <= CATALOGO es True porque todo lo de Ana está en el catálogo. (ANA & LUIS) < ANA también, porque lo común está dentro de lo de Ana y además Ana tiene más.

La afinidad de este programa tiene nombre propio: es el índice de Jaccard, y se usa de verdad en sistemas de recomendación para medir cuánto se parecen dos usuarios. En los ejercicios construirás un pequeño recomendador con ella.

Escribir len(ANA & LUIS) / len(ANA) + len(LUIS). Sin paréntesis, la división se hace antes que la suma, y además dividir entre 10 contaría dos veces lo común. El denominador correcto es la unión.

Pruébalo tú

  • Añade "Marzo" a LUIS. ¿Qué líneas de la salida cambian? Predice las siete antes de ejecutar.
  • ¿Qué pasaría con la afinidad si Ana y Luis tuvieran exactamente las mismas canciones? ¿Y si no compartieran ninguna? Calcúlalo a mano y luego compruébalo.
  • Escribe todas_menos(conjuntos, excluido) que reciba un diccionario usuario → conjunto y devuelva la unión de los conjuntos de todos menos el excluido.

Programa 3 · La biblioteca: usuarios, playlists y canciones

Para terminar, una estructura más realista: un diccionario de usuarios donde cada usuario tiene sus playlists, y cada playlist es una lista de códigos de canción. La información de cada canción está en otro diccionario, CANCIONES, y el código es el puente entre los dos.

biblioteca.py

CANCIONES = {
    "c01": {"titulo": "Luz de neón", "artista": "Marea Alta", "segundos": 215},
    "c02": {"titulo": "Café a las 3", "artista": "Los Turnos", "segundos": 187},
    "c03": {"titulo": "Domingo lento", "artista": "Noa Vela", "segundos": 242},
    "c04": {"titulo": "Kilómetro 0", "artista": "Marea Alta", "segundos": 201},
    "c05": {"titulo": "Pixel", "artista": "Bit Bang", "segundos": 176},
}

USUARIOS = {
    "ana": {
        "nombre": "Ana",
        "playlists": {"Estudiar": ["c03", "c02"], "Gym": ["c01", "c05", "c04"]},
    },
    "luis": {
        "nombre": "Luis",
        "playlists": {"Coche": ["c04", "c01"]},
    },
}


def mm_ss(segundos):
    """Convierte 592 segundos en '9:52'."""
    return f"{segundos // 60}:{segundos % 60:02d}"


def duracion_playlist(usuario, nombre_playlist):
    """Duración total de una playlist de un usuario (0 si no existe)."""
    playlists = USUARIOS[usuario]["playlists"]      # bajamos dos niveles
    codigos = playlists.get(nombre_playlist, [])    # tercer nivel, con valor por defecto
    total = 0
    for codigo in codigos:
        total += CANCIONES[codigo]["segundos"]      # del código, a su duración
    return total


def artistas_de(usuario):
    """Conjunto de artistas que aparecen en las playlists del usuario."""
    artistas = set()                                # set(), no {}
    for codigos in USUARIOS[usuario]["playlists"].values():   # cada lista de códigos
        for codigo in codigos:
            artistas.add(CANCIONES[codigo]["artista"])        # el set no repite
    return artistas


def quien_tiene(codigo):
    """Nombres de los usuarios con esa canción en alguna playlist."""
    nombres = []
    for datos in USUARIOS.values():                 # no tenemos clave: hay que recorrer
        for codigos in datos["playlists"].values():
            if codigo in codigos:
                nombres.append(datos["nombre"])
                break                               # con una playlist basta
    return nombres


print(USUARIOS["ana"]["playlists"]["Gym"])
print("Gym de Ana:", mm_ss(duracion_playlist("ana", "Gym")))
print("Fiesta de Ana:", mm_ss(duracion_playlist("ana", "Fiesta")))
print("Artistas de Ana:", sorted(artistas_de("ana")))
print("Quién tiene c04:", quien_tiene("c04"))
print("Quién tiene c03:", quien_tiene("c03"))

Lánzalo con python3 biblioteca.py:

['c01', 'c05', 'c04']
Gym de Ana: 9:52
Fiesta de Ana: 0:00
Artistas de Ana: ['Bit Bang', 'Los Turnos', 'Marea Alta', 'Noa Vela']
Quién tiene c04: ['Ana', 'Luis']
Quién tiene c03: ['Ana']

Cómo está montada la estructura

La estructura de la biblioteca musicalUSUARIOS es un diccionario; dentro, el usuario ana es otro diccionario con su nombre y sus playlists; playlists es un diccionario de listas de códigos; cada código es una clave del diccionario CANCIONES.USUARIOSdict"ana"dict"luis": {…}"nombre": "Ana""playlists"dict"Gym"list"Estudiar": […]["c01", "c05", "c04"]CANCIONESdict"c05"dict"segundos": 176USUARIOS["ana"]["playlists"]["Gym"] → ["c01", "c05", "c04"]CANCIONES["c05"]["segundos"] → 176
Los códigos de la playlist son el puente entre los dos diccionarios: con uno de ellos consultas CANCIONES directamente.

La primera línea de la salida es la mejor forma de entenderlo. USUARIOS["ana"]["playlists"]["Gym"] baja tres niveles:

ExpresiónTipoValor
USUARIOS["ana"]dict{"nombre": "Ana", "playlists": {…}}
USUARIOS["ana"]["playlists"]dict{"Estudiar": […], "Gym": […]}
USUARIOS["ana"]["playlists"]["Gym"]list['c01', 'c05', 'c04']
CANCIONES["c05"]dict{"titulo": "Pixel", "artista": "Bit Bang", "segundos": 176}
CANCIONES["c05"]["segundos"]int176

Cuando escribas una expresión con muchos corchetes, haz esta tabla mentalmente: en cada paso pregúntate qué tipo tienes. Si es un dict, el siguiente corchete es una clave; si es una list, una posición.

duracion_playlist: tres niveles y un valor por defecto

def duracion_playlist(usuario, nombre_playlist):
    """Duración total de una playlist de un usuario (0 si no existe)."""
    playlists = USUARIOS[usuario]["playlists"]      # bajamos dos niveles
    codigos = playlists.get(nombre_playlist, [])    # tercer nivel, con valor por defecto
    total = 0
    for codigo in codigos:
        total += CANCIONES[codigo]["segundos"]      # del código, a su duración
    return total
  • La línea 28 baja dos niveles con corchetes normales: aquí suponemos que el usuario existe, y si no existiera preferimos el KeyError para enterarnos.
  • La línea 29 baja el tercer nivel con get(nombre_playlist, []). Si la playlist no existe, devuelve una lista vacía: el bucle no da ninguna vuelta y la duración es 0. Por eso «Fiesta de Ana» sale 0:00 en lugar de un error.
  • Dentro del bucle, cada código se convierte en su duración saltando al otro diccionario: CANCIONES[codigo]["segundos"].

Con la playlist Gym, el total va así: 215 (c01) + 176 (c05) + 201 (c04) = 592 segundos. mm_ss lo convierte: 592 // 60 son 9 minutos y 592 % 60 son 52 segundos. El formato :02d rellena con un cero a la izquierda cuando hace falta, para que 0 segundos salga 0:00 y no 0:0.

artistas_de: recorrer dos niveles y no repetir

def artistas_de(usuario):
    """Conjunto de artistas que aparecen en las playlists del usuario."""
    artistas = set()                                # set(), no {}
    for codigos in USUARIOS[usuario]["playlists"].values():   # cada lista de códigos
        for codigo in codigos:
            artistas.add(CANCIONES[codigo]["artista"])        # el set no repite
    return artistas
  • USUARIOS[usuario]["playlists"].values() da las listas de códigos de cada playlist (no nos interesan los nombres de las playlists, por eso usamos values() y no items()).
  • El segundo for recorre los códigos de cada lista, y cada código se convierte en un artista.
  • Usamos un conjunto porque Marea Alta sale dos veces (c01 y c04) y la queremos una sola vez. Con un set, el add repetido no hace nada y no hace falta ningún if.
  • Se crea con set(), no con {}: las llaves vacías serían un diccionario y el add fallaría.

quien_tiene: cuando no tienes la clave

def quien_tiene(codigo):
    """Nombres de los usuarios con esa canción en alguna playlist."""
    nombres = []
    for datos in USUARIOS.values():                 # no tenemos clave: hay que recorrer
        for codigos in datos["playlists"].values():
            if codigo in codigos:
                nombres.append(datos["nombre"])
                break                               # con una playlist basta
    return nombres

Esta función es la contraria de las anteriores: tenemos un código y queremos saber qué usuarios lo tienen. Los códigos no son claves de USUARIOS, están escondidos dentro de las listas, así que no hay atajo: hay que recorrer todos los usuarios y todas sus playlists.

  • El break corta el bucle de las playlists en cuanto encuentra el código. Sin él, si Ana tuviera c04 en dos playlists, su nombre saldría dos veces.
  • Con c04 salen Ana (en Gym) y Luis (en Coche). Con c03, solo Ana (en Estudiar).

Compara duracion_playlist con quien_tiene. La primera va directa con claves; la segunda recorre todo. Si en tu app la pregunta «¿quién tiene esta canción?» fuera muy frecuente, merecería la pena guardar también el diccionario al revés: código → conjunto de usuarios.

Pruébalo tú

  • Llama a duracion_playlist("pablo", "Gym"). ¿Qué error sale y en qué línea? ¿Cómo lo evitarías con get en el primer nivel?
  • Escribe titulos(usuario, nombre_playlist) que devuelva la lista de títulos (no códigos) de una playlist.
  • Escribe invertir(usuarios) que construya el diccionario código → conjunto de nombres de la nota anterior, y comprueba que con él quien_tiene se responde en una línea.

Míralo paso a paso en Python Tutor

Python Tutor ejecuta el código línea a línea y dibuja cada diccionario como una tabla de claves y valores. Elige Python 3 y pega este contador, que guarda la cuenta en una variable intermedia para que puedas verla:

historial = ["Pixel", "Marzo", "Pixel"]
cuentas = {}
for cancion in historial:
    actual = cuentas.get(cancion, 0)
    cuentas[cancion] = actual + 1
print(cuentas)

Salida esperada:

{'Pixel': 2, 'Marzo': 1}

Qué observar, paso a paso:

  1. Antes del bucle, cuentas aparece como un diccionario vacío: una caja sin filas.
  2. En la primera vuelta, cancion vale "Pixel" y actual vale 0: get no ha encontrado la clave y ha devuelto el valor por defecto. Fíjate en que cuentas sigue vacío después del get, porque get solo lee.
  3. En la línea siguiente aparece la primera fila del diccionario: "Pixel" → 1.
  4. En la segunda vuelta pasa lo mismo con "Marzo". En la tercera, "Pixel" otra vez: ahora actual vale 1, y la fila de Pixel cambia su valor a 2 en lugar de crear una fila nueva.
  5. Cuando termina el bucle, el diccionario tiene dos filas en el orden en que aparecieron las canciones por primera vez.

Para terminar

Hoy has usado los contenedores asociativos en tres situaciones distintas: un diccionario contador y un diccionario de listas para resumir un historial, conjuntos para comparar gustos con una sola operación y diccionarios anidados con códigos que hacen de puente entre dos estructuras. Si algo no te ha encajado, vuelve a la teoría; y cuando te sientas cómodo, pasa a los ejercicios, donde construirás un pequeño recomendador y cazarás tres fallos muy típicos.

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

¿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 *