Итоговое консольное приложение
Содержание курса
Главный цикл меню и двойное хранилище заметок
Каркас приложения — это два решения, принятых до того, как будут написаны конкретные функции. Первое: как программа вообще «слушает» пользователя. Второе: где и как хранятся заметки внутри.
Главный цикл
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 всегда содержат одни и те же заметки, просто организованные по-разному: один по порядку, другой по ключу. Когда этот каркас готов, можно переходить к деталям: файловым операциям и обработке ошибок.
