En aquest post construirem un quiz en Python amb una idea central: convertir un exercici de preguntes en un programa amb estructura, puntuació i feedback. El quiz és una excusa perfecta per practicar disseny de dades (com guardem les preguntes), control de flux i funcions.
A més, aquest projecte té dos modes: Normal (feedback immediat) i Examen (responem tot i corregim al final). En aquest post ens centrarem en construir una base sòlida i deixarem el mode examen com a extensió natural (que abordarem com a següent post de la sèrie).
Objectius d’aprenentatge
- Modelar preguntes i respostes amb estructures de dades.
- Implementar puntuació i feedback coherent.
- Separar lògica (funcions) de la part interactiva.
- Preparar l’arquitectura per afegir el mode examen.
1) Disseny del model de dades: què és una “pregunta”
Abans d’escriure codi, definim què necessitem guardar per a cada pregunta. En un quiz de tipus test (A/B/C/D) acostuma a ser suficient:
- enunciat (text de la pregunta)
- opcions (llista de respostes possibles)
- resposta correcta (índex o lletra)
- explicació (feedback curt quan corregim)
Per mantenir-ho simple i transparent, podem representar cada pregunta amb un diccionari. Això ens dóna flexibilitat i llegibilitat.
PREGUNTES = [
{
"text": "Quina estructura és immut able en Python?",
"choices": ["llista", "diccionari", "tupla", "set"],
"answer": 2,
"explain": "Les tuples són immutables: no podem afegir, eliminar o modificar elements."
},
]Decisió important: aquí guardem answer com a índex (0, 1, 2, 3). Això simplifica la correcció i evita errors amb lletres.
2) Funcions base: mostrar preguntes i validar resposta
Quan un programa creix, ens convé separar responsabilitats. En un quiz, dues funcions molt útils són:
mostrar_pregunta(): imprimeix l’enunciat i les opcions de manera clara.demanar_resposta(): llegeix l’entrada i aplica validació (evitem entrades buides o fora de rang).
def mostrar_pregunta(q: dict, num: int, total: int) -> None:
print(f"\nPregunta {num}/{total}")
print(q["text"])
for i, opcio in enumerate(q["choices"], start=1):
print(f" {i}) {opcio}")
def demanar_resposta(n_opcions: int) -> int:
while True:
entrada = input("Resposta (1-{}): ".format(n_opcions)).strip()
if not entrada:
print("Entrada buida. Escriu un número.")
continue
if not entrada.isdigit():
print("Entrada no vàlida. Escriu un número.")
continue
valor = int(entrada)
if not (1 <= valor <= n_opcions):
print("Fora de rang. Torna-ho a provar.")
continue
return valor - 1 # convertim a índex 0..n-1Fixem-nos que convertim l’entrada (1..n) a índex (0..n-1). Això fa que la comparació amb q["answer"] sigui directa.
3) Mode Normal: correcció i feedback immediat
En el mode normal, després de cada resposta corregim al moment. Això és ideal per aprendre perquè el feedback arriba quan encara tenim la pregunta “fresca”.
Una correcció mínima però útil inclou:
- si és correcte o no
- quina era la resposta correcta
- una explicació breu (si en tenim)
def corregir(q: dict, resposta: int) -> tuple[bool, str]:
correcta = (resposta == q["answer"])
correcta_text = q["choices"][q["answer"]]
if correcta:
return True, f"✅ Correcte! ({correcta_text})"
missatge = f"❌ Incorrecte. La correcta era: {correcta_text}"
if q.get("explain"):
missatge += f"\n Motiu: {q['explain']}"
return False, missatge4) Puntuació: comptar encerts de manera robusta
La puntuació més habitual en un quiz de base és el nombre d’encerts. Podem guardar:
encerts(enter)total(len de la llista de preguntes)- percentatge final
def percentatge(encerts: int, total: int) -> float:
if total == 0:
return 0.0
return 100.0 * encerts / total5) Codi complet (quiz en mode Normal, consola)
A continuació unim totes les peces en un programa executable. Aquesta versió ja és una base bona per portar-la després a Streamlit (quan canviem input() per botons i widgets).
PREGUNTES = [
{
"text": "Quina estructura és immutable en Python?",
"choices": ["llista", "diccionari", "tupla", "set"],
"answer": 2,
"explain": "Les tuples són immutables: no podem modificar els seus elements."
},
{
"text": "Quina funció retorna la longitud d'una llista?",
"choices": ["size()", "count()", "len()", "length()"],
"answer": 2,
"explain": "En Python fem servir len() per obtenir la longitud d'una seqüència."
},
{
"text": "Quin tipus de dada guarda valors únics sense ordre?",
"choices": ["set", "list", "tuple", "dict"],
"answer": 0,
"explain": "Un set no admet duplicats i no garanteix ordre."
},
]
def mostrar_pregunta(q: dict, num: int, total: int) -> None:
print(f"\nPregunta {num}/{total}")
print(q["text"])
for i, opcio in enumerate(q["choices"], start=1):
print(f" {i}) {opcio}")
def demanar_resposta(n_opcions: int) -> int:
while True:
entrada = input("Resposta (1-{}): ".format(n_opcions)).strip()
if not entrada:
print("Entrada buida. Escriu un número.")
continue
if not entrada.isdigit():
print("Entrada no vàlida. Escriu un número.")
continue
valor = int(entrada)
if not (1 <= valor <= n_opcions):
print("Fora de rang. Torna-ho a provar.")
continue
return valor - 1
def corregir(q: dict, resposta: int) -> tuple[bool, str]:
correcta = (resposta == q["answer"])
correcta_text = q["choices"][q["answer"]]
if correcta:
return True, f"✅ Correcte! ({correcta_text})"
missatge = f"❌ Incorrecte. La correcta era: {correcta_text}"
if q.get("explain"):
missatge += f"\n Motiu: {q['explain']}"
return False, missatge
def percentatge(encerts: int, total: int) -> float:
return 0.0 if total == 0 else 100.0 * encerts / total
def jugar_quiz_normal(preguntes: list[dict]) -> None:
total = len(preguntes)
encerts = 0
print("=== Quiz (Mode Normal) ===")
print("Responem i corregim al moment.\n")
for i, q in enumerate(preguntes, start=1):
mostrar_pregunta(q, i, total)
resp = demanar_resposta(len(q["choices"]))
ok, msg = corregir(q, resp)
print(msg)
if ok:
encerts += 1
print("\n=== Resultat final ===")
print(f"Encerts: {encerts}/{total} ({percentatge(encerts, total):.1f}%)")
if __name__ == "__main__":
jugar_quiz_normal(PREGUNTES)6) Preparar el mode Examen
El mode examen no canvia les preguntes, però sí el flux:
- En lloc de corregir cada pregunta, guardem les respostes.
- Quan hem respost tot, fem una passada de correcció i mostrem el resultat final.
- Podem mostrar un resum: preguntes fallades, correcta vs resposta, explicació, etc.
Això és interessant perquè ens obliga a separar clarament dues fases: jugar (respondre) i corregir (avaluar). És el que farem al següent post de la sèrie.

Aquest quiz forma part de DeGalaLab Arcade, una col·lecció de mini jocs fets amb Python i portats a web amb Streamlit.
⏭️ Següent post de la sèrie: Mode examen: separar jugar de corregir (guardar respostes i avaluar al final).