v1.0.0

Python с нуля для ЕГЭ · Сохраняем дела между запусками

Урок курсаPython 35 задачБесплатно

Сохраняем дела между запусками

Запишем дела в JSON и вернём их после нового запуска, даже если файла пока нет.

Теория

Почему без файла не обойтись

Пока программа запущена, список хранится в памяти. Закроем терминал — и все дела исчезнут. Чтобы список действительно пригодился, будем записывать его после каждого изменения, а при новом запуске — читать обратно из файла.

JSON подходит без дополнительных преобразований: он умеет хранить списки, словари, строки, числа и логические значения. Стандартный модуль json запишет наши объекты Python в текст, а потом восстановит их.

import json with open("tasks.json", "w", encoding="utf-8") as file:    json.dump(tasks, file, ensure_ascii=False, indent=2)

Что делать, если файла ещё нет

При самом первом запуске файла tasks.json ещё нет — и это нормально. Перехватим только FileNotFoundError и начнём с пустого списка. Другие ошибки не прячем: если код сломан, нам всё ещё нужен traceback.

def load_tasks():    try:        with open("tasks.json", "r", encoding="utf-8") as file:            return json.load(file)    except FileNotFoundError:        return []
Разберём на примере
Что меняется между двумя запусками
Сначала файла нет, затем пользователь добавляет дело и запускает программу снова.
  1. В первый раз load_tasks не находит файл и возвращает пустой список.
  2. После добавления дела save_tasks создаёт tasks.json.
  3. При следующем запуске json.load читает сохранённый список.
  4. Команда list показывает прежнее дело — вводить его заново не нужно.
Проверьте себя
Почему отсутствие tasks.json можно безопасно превратить в пустой список?
Это известное состояние первого запуска; программа умеет начать работу без прежних дел.

Не выдаём уже занятый номер

После перезапуска нельзя снова ставить next_id = 1: такой номер уже может быть в файле. Найдём самый большой сохранённый id и прибавим единицу. Для пустого списка зададим default=0, поэтому первым номером снова станет 1.

next_id = max((task["id"] for task in tasks), default=0) + 1

Собираем версию с сохранением

Теперь save_tasks вызывается после успешного добавления, отметки, редактирования или удаления. Команда list ничего не меняет, поэтому записывать файл после неё незачем.

import json FILE_NAME = "tasks.json" def load_tasks():    try:        with open(FILE_NAME, "r", encoding="utf-8") as file:            return json.load(file)    except FileNotFoundError:        return [] def save_tasks(tasks):    with open(FILE_NAME, "w", encoding="utf-8") as file:        json.dump(tasks, file, ensure_ascii=False, indent=2) tasks = load_tasks()next_id = max((task["id"] for task in tasks), default=0) + 1 def find_task(task_id):    for task in tasks:        if task["id"] == task_id:            return task    return None def show_tasks():    if not tasks:        print("Список пока пуст")        return    for task in tasks:        mark = "x" if task["done"] else " "        print(f'[{mark}] {task["id"]}. {task["title"]}') def add_task():    global next_id    title = input("Новое дело: ").strip()    if not title:        print("Название не может быть пустым")        return    tasks.append({"id": next_id, "title": title, "done": False})    next_id += 1    save_tasks(tasks) def complete_task():    task = find_task(int(input("Номер дела: ")))    if task is None:        print("Дело не найдено")        return    task["done"] = True    save_tasks(tasks) def edit_task():    task = find_task(int(input("Номер дела: ")))    if task is None:        print("Дело не найдено")        return    title = input("Новый текст: ").strip()    if title:        task["title"] = title        save_tasks(tasks) def delete_task():    task = find_task(int(input("Номер дела: ")))    if task is None:        print("Дело не найдено")        return    tasks.remove(task)    save_tasks(tasks) while True:    command = input("Команда: ").strip().lower()    if command == "list":        show_tasks()    elif command == "add":        add_task()    elif command == "done":        complete_task()    elif command == "edit":        edit_task()    elif command == "delete":        delete_task()    elif command == "exit":        break    else:        print("Неизвестная команда")
Как действовать
Проверяем работу после перезапуска
  1. Удалите старый тестовый файл. Первый запуск должен начаться с пустого списка.
  2. Добавьте и отметьте дело. Откройте tasks.json и найдите оба значения.
  3. Запустите программу снова. Список и отметка должны восстановиться.
  4. Добавьте новое дело. Его id должен быть больше всех сохранённых.
Проверьте себя
После каких команд нужно сохранять файл?
После успешного добавления, отметки, редактирования или удаления — то есть после реального изменения tasks.

Практика

Какой модуль работает с JSON

Какой стандартный модуль Python нужен, чтобы читать и записывать JSON?

Подсказка
Модуль называется так же, как сам формат.
Решение

Стандартный модуль json предоставляет функции load и dump.

Как записать список в JSON

Какая функция модуля json записывает tasks в уже открытый файл?

Подсказка
В коде она получает два аргумента: tasks и file.
Решение

json.dump(tasks, file, ...) сериализует список и записывает JSON в файл.

Как прочитать список из JSON

Какая функция модуля json читает открытый файл и возвращает объекты Python?

Подсказка
Это обратная операция к dump.
Решение

json.load(file) читает JSON и восстанавливает список словарей.

Что происходит при первом запуске

Какое исключение перехватывает load_tasks, если tasks.json ещё не создан?

Подсказка
Название исключения буквально означает «файл не найден».
Решение

Отсутствующий файл вызывает FileNotFoundError; функция возвращает пустой список.

Продолжаем нумерацию после перезапуска

Сохранённые дела имеют id 2 и 5. Какое значение получит next_id после загрузки?

Подсказка
Найдите самый большой сохранённый id и прибавьте единицу.
Решение

Максимальный сохранённый id равен 5, поэтому следующий равен 6.

Итог

Теперь программу можно закрыть и открыть снова: дела, отметки и номера восстановятся из файла.

В последнем уроке проверим неприятные случаи: слово вместо номера, неизвестный id и повреждённый JSON. Затем пройдём весь сценарий так, как его проходит обычный пользователь.

Прогресс

0 / 5
x

Вы ещё не решали задания