Итоговый проект

Итоговое консольное приложение

Содержание курса

Главный цикл меню и двойное хранилище заметок

Каркас приложения — это два решения, принятых до того, как будут написаны конкретные функции. Первое: как программа вообще «слушает» пользователя. Второе: где и как хранятся заметки внутри.

Главный цикл

while True запускает бесконечный цикл, который завершается только явным break. Внутри цикла одно: вызов input(), который ждёт команду от пользователя, а затем ветвление if/elif/else по тому, что он ввёл.

while True:
    command = input("Команда (добавить/показать/удалить/сохранить/выход): ").strip().lower()

    if command == "добавить":
        add_note(notes_list, notes_dict)
    elif command == "показать":
        show_notes(notes_list)
    elif command == "удалить":
        delete_note(notes_list, notes_dict)
    elif command == "сохранить":
        save_notes(notes_list)
    elif command == "выход":
        save_notes(notes_list)
        break
    else:
        print("Неизвестная команда, попробуйте ещё раз.")

.strip().lower() здесь — это маленькая защита: пользователь может случайно добавить пробел или написать «Добавить» с заглавной буквы, и без нормализации ввода цикл попадёт в else. После этого управление возвращается на начало цикла и программа снова ждёт ввода.

Когда пользователь вводит «выход», программа сначала сохраняет данные, а затем выходит через break. Порядок важен: если сначала break, данные не сохранятся.

Двойное хранилище

Заметки хранятся одновременно в двух структурах: списке и словаре. Это не избыточность — у каждой своя задача.

Список даёт порядок. Когда пользователь просит «показать все заметки», нужно вывести их в той последовательности, в которой они добавлялись. Список это гарантирует.

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

notes_list = []       # хранит заметки по порядку
notes_dict = {}       # хранит заметки по id для быстрого доступа

# Добавление новой заметки
def add_note(notes_list, notes_dict):
    text = input("Текст заметки: ").strip()
    note_id = f"note_{len(notes_list) + 1}"
    note = {"id": note_id, "text": text}
    notes_list.append(note)
    notes_dict[note_id] = note
    print(f"Заметка добавлена с id: {note_id}")

# Удаление по id
def delete_note(notes_list, notes_dict):
    note_id = input("Id заметки для удаления: ").strip()
    if note_id in notes_dict:
        note = notes_dict.pop(note_id)
        notes_list.remove(note)
        print(f"Заметка {note_id} удалена.")
    else:
        print("Заметка с таким id не найдена.")

Оба контейнера инициализируются один раз — до главного цикла. Функции принимают их как параметры и изменяют на месте.

Важно понять, почему это работает. Когда Python передаёт список или словарь в функцию, функция получает ссылку на тот же объект, что живёт в main(). Методы append, remove, pop и присваивание по ключу словаря меняют этот объект на месте — поэтому изменения видны снаружи. Если же внутри функции написать notes_list = ..., локальное имя начнёт указывать на новый объект, а оригинал в main() останется нетронутым. Именно об этой ловушке пойдёт речь в разделе про типичные ошибки.

В итоге notes_list и notes_dict всегда содержат одни и те же заметки, просто организованные по-разному: один по порядку, другой по ключу. Когда этот каркас готов, можно переходить к деталям: файловым операциям и обработке ошибок.