Transformar erros em uma rodada de prática

PythonBeginner
Pratique Agora

Introdução

Salve respostas estáveis e transforme somente os erros desta rodada em prática independente. Você precisa dos conceitos anteriores do quiz e de funções, listas, dicionários, condições e laços Python. Esta unidade prepara as três regras anteriores de forma independente e não depende de arquivos ou processos de outra unidade.

Use WebIDE Terminal → New Terminal (More em janelas estreitas). Edite apenas logic.py; preserve engine.py, quiz.py e bank.json. Tempo estimado de leitura e prática: 35 minutos, não medido com iniciantes.

Salvar respostas enviadas como JSON

Nesta etapa, salve uma rodada real sem prender as respostas às posições na tela. O mecanismo preparado retorna uma lista contendo somente pares question_id/option_id enviados. Ele cuida do retorno e da pontuação; o registro não declara sua própria correção nem contém números da tela.

Comece no terminal WebIDE:

cd /home/labex/project

json.dump escreve listas e dicionários Python como JSON. with open(..., "w", encoding="utf-8") abre um arquivo de texto para escrita e o fecha depois. UTF-8 mantém o texto portável; ensure_ascii=False deixa Unicode legível e indent=2 facilita a leitura. stream.write("\n") termina com uma quebra de linha. A estrutura externa {"answers": answers} identifica a lista.

O here-document completo preserva as três regras prontas e adiciona a função de gravação. > substitui logic.py; <<'PY', entre aspas, escreve o texto literal até o último PY. A função de prática será adicionada na próxima etapa.

cat > logic.py <<'PY'
import json


def choose_questions(questions, count, rng):
    return rng.sample(questions, min(count, len(questions)))


def move_options(question, rng):
    options = question["options"].copy()
    rng.shuffle(options)
    return options


def read_answer(options, ask, tell):
    while True:
        text = ask("Answer (number or q): ").strip()
        if text.lower() == "q":
            return None
        for position, option in enumerate(options, start=1):
            if text == str(position):
                return option["option_id"]
        tell("Enter one displayed number, or q to stop.")


def save_answers(path, answers):
    with open(path, "w", encoding="utf-8") as stream:
        json.dump({"answers": answers}, stream, ensure_ascii=False, indent=2)
        stream.write("\n")
PY

--ordered mantém a ordem conhecida de perguntas e alternativas; --output dá nome a um registro novo. Caminhos de saída existentes são rejeitados antes da partida, protegendo registros anteriores. Responda duas perguntas: 1 para sky (correto), depois 1 para tide (incorreto).

python3 quiz.py bank.json --ordered --count 2 --output round.json
Score: 1/2 answered
Saved round.json

cat mostra o registro real:

cat round.json

Ele contém sky/sun e tide/sun. O mesmo ID de alternativa tem significado diferente em cada pergunta. Preserve esta rodada original. Se executar novamente por engano, o erro de saída existente é esperado: use um novo nome para experiências extras, sem sobrescrevê-la. Se errar o nome de origem, consulte ls e execute novamente com o caminho correto; Quiz error: ... não significa que uma rodada foi salva.

Praticar apenas os erros desta rodada

Nesta etapa, recalcule os erros pelo verdadeiro ID correto de cada pergunta. Um dicionário associa os IDs de perguntas respondidas aos IDs das alternativas escolhidas. Inclua uma pergunta somente se foi respondida e sua resposta difere do próprio correct_option_id. Exclua tanto as corretas quanto as não respondidas. A lista preserva objetos de pergunta inteiros. Em {answer["question_id"]: answer["option_id"] for answer in answers}, a parte depois de for percorre as respostas e a parte chave/valor anterior constrói uma entrada: é uma compreensão de dicionário. Em [question for question in questions if ...], for percorre perguntas e if mantém só os objetos correspondentes: é uma compreensão de lista. and exige ambas as condições: a resposta existe e está errada. Assim, uma pergunta sem resposta não conta como erro. >> acrescenta sem substituir as regras existentes:

cat >> logic.py <<'PY'
def wrong_questions(questions, answers):
    selected = {answer["question_id"]: answer["option_id"] for answer in answers}
    return [question for question in questions
            if question["question_id"] in selected
            and selected[question["question_id"]] != question["correct_option_id"]]
PY

--practice lê a rodada original; --output escreve outro registro novo separado. A prática repete todos e somente os erros daquela rodada, não um histórico acumulado. Execute:

python3 quiz.py bank.json --practice round.json --output retry.json

Aparece somente tide. Escolha o número exibido de Moon, cuja posição pode mudar. O resultado é Score: 1/1 answered. Inspecione os dois registros independentes:

cat round.json retry.json

O original continua com tide/sun; a prática tem tide/moon. O banco não mudou. Agora crie uma rodada totalmente correta. A quantidade é positiva e a seleção é limitada por min(count, len(questions)); 100 faz cada uma das três perguntas uma vez.

python3 quiz.py bank.json --ordered --count 100 --output perfect.json

Digite 1, 2, 2 para sky, tide e roots. Depois pratique essa rodada:

python3 quiz.py bank.json --practice perfect.json --output empty.json
No mistakes to practice.
Score: 0/0 answered
Saved empty.json

Um conjunto vazio de erros é válido. Por fim, comece outra rodada e digite q antes de responder:

python3 quiz.py bank.json --output stopped.json

A pontuação é 0/0 e stopped.json tem uma lista answers vazia. Não deve registrar como erro a pergunta abandonada. Preserve os cinco registros. Se a edição causar erro de sintaxe, repita o bloco completo da etapa; como a etapa 1 substitui o arquivo, execute novamente depois o bloco de acréscimo da etapa 2. Mantenha registros existentes e use nomes novos para mais partidas.

Resumo

Você salvou pares de ID realmente enviados, manteve a rodada original e criou prática independente de erros, incluindo rodadas vazias e interrompidas válidas.