Превратить ошибки в отдельную тренировку

PythonBeginner
Практиковаться сейчас

Введение

Сохраните устойчивые ответы и превратите только ошибки этого раунда в отдельную тренировку. Нужны понятия предыдущей викторины, функции, списки, словари, условия и циклы Python. Эта часть независимо подготавливает три предыдущих правила и не требует файлов или процессов прошлой части.

Используйте WebIDE Terminal → New Terminal (More в узком окне). Меняйте только logic.py; сохраните engine.py, quiz.py и bank.json. Чтение и практика займут примерно 35 минут; это не измеренное время начинающих.

Сохранить отправленные ответы в JSON

На этом шаге сохраните реальный раунд, не связывая ответы с экранными позициями. Движок возвращает список только отправленных пар question_id/option_id. Он отвечает за обратную связь и баллы; запись не содержит самостоятельно объявленной правильности или экранного номера.

Начните в терминале WebIDE:

cd /home/labex/project

json.dump записывает списки и словари Python в JSON. with open(..., "w", encoding="utf-8") открывает текстовый файл для записи и затем закрывает его. UTF-8 обеспечивает переносимость текста; ensure_ascii=False оставляет Unicode читаемым, а indent=2 делает файл удобным для чтения. stream.write("\n") добавляет завершающий перевод строки. Оболочка {"answers": answers} обозначает список.

Полный here-document сохраняет три готовых правила и добавляет функцию записи. > заменяет logic.py; заключённый в кавычки <<'PY' записывает буквальный текст до последнего PY. Функция тренировки добавляется на следующем шаге.

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 сохраняет известный порядок вопросов и вариантов; --output задаёт новую запись. Существующий выходной путь отклоняется до игры, защищая старые записи. Ответьте на два вопроса: 1 для sky (верно), затем 1 для tide (неверно).

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

cat показывает настоящую запись:

cat round.json

Она содержит sky/sun и tide/sun. Один ID варианта имеет разный смысл в разных вопросах. Сохраните исходный раунд. При случайном повторном запуске ошибка существующего выхода ожидаема: выберите новое имя для дополнительных опытов, не перезаписывая файл. При опечатке в имени источника посмотрите ls и повторите правильный путь; Quiz error: ... не означает, что раунд сохранён.

Повторять только ошибки этого раунда

На этом шаге заново определите ошибки по настоящему правильному ID каждого вопроса. Словарь сопоставляет ID отвеченных вопросов выбранным ID вариантов. Включайте вопрос, только если ответ был дан и отличается от его собственного correct_option_id. Верные и неотвеченные вопросы исключаются. Список сохраняет объекты вопросов целиком. В {answer["question_id"]: answer["option_id"] for answer in answers} часть после for обходит ответы, а предшествующая пара ключ/значение создаёт запись словаря. Это словарное включение. В [question for question in questions if ...] конструкция for обходит вопросы, а if оставляет подходящие объекты: это списковое включение. and требует обоих условий: ответ существует и он неверен. Поэтому вопрос без ответа не станет ошибкой. >> дописывает, не заменяя готовые правила:

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 читает исходный раунд, --output создаёт отдельную новую запись. Повторяются все и только ошибки этого раунда, а не накопленная история. Выполните:

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

Появится только tide. Выберите показанный номер Moon: позиция может измениться. Результат — Score: 1/1 answered. Просмотрите две независимые записи:

cat round.json retry.json

В исходной по-прежнему tide/sun, в повторной — tide/moon. Банк не изменился. Теперь создайте полностью правильный раунд. Количество положительно, а выборка ограничена min(count, len(questions)); 100 задаст каждый из трёх вопросов один раз.

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

Введите 1, 2, 2 для sky, tide и roots. Затем запустите тренировку этого раунда:

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

Пустое множество ошибок допустимо. Наконец, начните новый раунд и введите q, не дав ответа:

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

Баллы равны 0/0, а список answers в stopped.json пуст. Нельзя сохранять оставленный вопрос как ошибочный ответ. Сохраните все пять записей. Если редактирование вызвало синтаксическую ошибку, повторите полный блок шага; поскольку шаг 1 заменяет файл, после него снова выполните блок дописывания шага 2. Существующие записи сохраняйте, а для новых игр выбирайте другие имена.

Резюме

Вы сохранили действительно отправленные пары ID, оставили исходный раунд и создали отдельную тренировку ошибок, включая допустимые пустые и прерванные раунды.