От input() к UI после вводного курса по Python

Неоригинальное начало.

Начинающий python разработчик недавно попросил меня посмотреть «что нужно улучшить» в программе, написанной им по гайду «Python с нуля». Таких гайдов, курсов и туториалов доступно великое множество.

Консольное приложение ведения списка пользователей. Вывести список, добавить, отредактировать, удалить пользователя.

Парень не ИТ-ник, учится на экономической специальности. Приложение было написано аккуратно и работало.

В чем проблема? Интерфейс пользователя.

Конечно, в коде были участки для рефакторинга. Где ж и их нет. 

Но зададим себе вопрос:, а вы бы хотели пользоваться таким приложением?   Ответ очевиден — нет. И причина в UI (интерфейсе пользователя). 

Консольные интерактивные интерфейсы пользователя были актуальны для mainframe в 80-х годах прошлого века, а то и ранее. 

Командная строка живее всех живых. Но паттерн использования другой. Утилиты командной строки запускаются, выполняют задачу, выводят результат и быстро завершаются

Ближайшим аналогом разработанного приложения являются утилиты командной строки для управления пользователями операционной системы. Системному администратору хорошо. А вот обычным пользователям такое приложение «не зайдет».

Где tutorial по самому крутому UI? Не крутой, а доступный тебе для освоения.

Казалось бы решение лежит на поверхности. 

Не надо усложнять
Не надо усложнять

Но почему в ходе учебного курса предложили сделать именно такой интерфейс?  

Все просто. Консольный интерфейс с использованием вызовов input () крайне прост по своей сути. Это позволяет обучающемуся сосредоточиться на понимании базовых структур данных и конструкций языка.

Ограничение в скорости усвоения знаний.

Сложность использования технологий растет до тех пор, пока не найдется способ
Сложность использования технологий растет до тех пор, пока не найдется способ «упаковаться» внуть фреймворка или библиотеки. Но и упаковка не 100% панацея

Современные ИТ-технологии не сразу стали такими. С каждым витком развития происходило их усложнение. В какие то моменты времени «сложность пряталась» внутрь библиотек и фреймворков. 

Даже общее понимание идей и концепций при наличии готовых библиотек требует времени.

А скорость усвоения знаний человеком весьма конечна

Применительно к нашему кейсу: изучив базовый курс по Python, у новичка не получится в короткий срок научиться делать интерфейсы общения с пользователем на естественном языке, как у ИИ.

Вряд ли получится сделать современный Web интерфейс на React/Vue.js и аналогичных Javascript фреймворках. 

Может быть получится сделать классический Web интерфейс c генерацией статичного HTML на серверной стороне. 

А вот десктоп приложение с графическим оконным интерфейсом (GUI) сделать скорее всего получится.

Десктоп приложения с оконным интерфейсом были следующей примечательной стадией развития способов взаимодействия с пользователем. Их расцвет пришелся на 1990-е годы.

Выбираю GUI-библиотеку и делаю? Рано. Хороший фасад не бывает без хорошей изнанки.

GUI приложение станет сложнее, чем консольное. К этому нужно приготовиться.

Хочешь писать сложные программы — учись их структурировать.

А.П. Чехов. Несуществующие цитаты из сети интернет.
А.П. Чехов. Несуществующие цитаты из сети интернет.

Структура GUI приложения. Разделяй и властвуй.

Показанная структура достаточно практична, но не является единственно верной «серебряной пулей».

Постарайся понять подход и идею.

Годная структура  GUI приложения. Не догма
Годная структура GUI приложения. Не догма

Фронтальная часть приложения (front или фронт) занимается отображением и вводом данных пользователя.

Подложка (back или бэк) реализует логику обработки данных. В нашем случае создание, удаление, редактирование, получение и хранение списка пользователей.

Фронт про отображение, бэк про логику обработки.

Бэк состоит из:

  • Model — набор сущностей предметной области. В нашем случае одна сущность User (Пользователь).

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

  • Validator — логика проверки корректности единичной сущности. Используется Service.

  • Repository — хранилище списка пользователей. Используется Service

Структура приложения является разновидностью паттерна проектирования MVC (Model-View-Controller)

Кончай дедукцию, давай продукцию.

С целью и способом достижения определились. Пора начинать реализацию.

Я подсказывал начинающему программисту куда двигаться, но намеренно не писал готовый код.

Продолжу данный подход и в статье.

Какие знания обновить.

Потребуется обновить (или приобрести) базовые знания ООП (объектно-ориентированного программирования): класс, конструктор класса, методы, поля, понимание наследования. 

Полезно иметь начальные знания по теме модулей в python. Желательно уметь пользоваться type hints.

Модель и валидатор. На страже корректности полей.

Модель (model) состоит из одного класса User.

class User:
  def __init__(self, id: int, first_name: str, last_name: str):
    self.id = id # уникальный идентификатор пользователя
    self.first_name = first_name # имя пользователя
    self.last_name = last_name # фамилия пользователя

Валидатор (validator) — Проверяет фамилию и имя на заполненение данными и наличие целочисленного id у пользователя.

class UserValidator:
  def validate(self, user: User):
    # Проверки:
    # Имя и фамилия должны быть непустой строкой 
    # без цифр и спец символов
    # id должно быть целым числом
    ... 
    return [] # вернуть пустой список, если ошибки не обнаружены

Репозиторий. Храни и выбирай.

Исходная программа хранила список пользователей в памяти. Был реализован репозитарий (repository) — обертка над питоновским списком с автогенерацией id пользователя при вставке.

class UserRepository:
  def add_user(self, user: User): 
    # сгенерировать id, user.id = gen_id
    # сохранить пользователя в списке
    pass
  def update_user(self, user: User):
    # если пользователь не найден по id, то ошибка
    pass
  def delete_user(self, user: User):
    pass
  def list_all(self):
    # вернуть список пользователей
    pass
  def find_by_id(self, id: int) -> User | None:
    # Найти пользователя по id
    pass
  def find_by_names(self, first_name: str, last_name: str):
    # True - есть пользователь с таким именем и фамилией, иначе False 
    pass

Сервис бэку голова.

Сервис (service) — Реализует возможность создания, редактирования, удаления пользователя, а также получение списка пользователей. Использует валидатор и репозиторий.

class UserService:
  def __init__(self, validator: UserValidator, repo: UserRepository):
    self.validator = validator
    self.repo = repo

  def create_user(self, first_name: str, last_name: str):
    user = User(0, first_name, last_name)
    errors = self.validator.validate(user)
    # Если найдены ошибки в атрибутах пользователя, 
    # то вернуть ошибку
    ...
    founded_user = repo.find_by_names(first_name, last_name)
    # если пользователь с именем и фамилией уже есть - ошибка
    ...

    # все ок. добавляем пользователя в список пользователей
    self.repo.add_user(user)

    return user
  
  def update_user(self, user: User):
    # Проверть, что пользователь с id есть в списке
    existed_user = self.repo.find_by_id(user.id)
    if existed_user is None:
      ...
    # проверить валидность обновляемого пользователя
    errors = self.validator.validate(user)
    if len(errors) > 0:
      ...

    # проверить, что нет пользователя с другим id, 
    # но таким же именем и фамилией
    self.repo.find_by_names(user.first_name, user.last_name)
    
    self.repo.update_user(user)
  
  def delete_user(self, user: User):
    pass
  
  def list_all_users(self):
    return self.repo.list_all()

В качестве промежуточного результата консольное приложение было отрефакторено для использования бэка без изменения консольного UI.

Библиотека UI. PySide.

Для реализации фронта выбрали фреймворк PySide по причине популярности и большого количества гайдов в сети.

Главный экран GUI был написан с помощью виджета QtTable, кнопок QPushButton. Формы создания и редактирования пользователя построены на базе QDialog.

Трудности первых шагов были успешно преодолены.

4644ff14b57f42b02ce971f289022f0e.jpg

Что в итоге? Шаг из 80-х в 90-е за три недели.

В течении трех недель самостоятельных занятий по вечерам и созвонов раз два-три дня приложнение приобрело не идеальный, но работающий GUI и бэк.

Стало ли приложение лучше? Да.

Для конечного пользователя качественный скачок очевиден. С точки зрения программирования непаханое поле отзывов «да я сделаю проще и лучше в сто раз».

Является ли предложенный путь «единственно правильным»? Нет. Всего лишь один из вполне рабочих вариантов.

Главная ценность кейса в возможности за две-три недели после базового курса по python повысить практическую применимость навыков.

Плохая новость в том, что достичь мастерства легко и за один подход не получится.

Хороших новостей больше:

  • Писать десктопные приложения не так страшно. Хотя и далеко не всегда актуально.

  • Целеполагание («нужно делать правильные вещи»), проектирование («нужно делать вещи правильно») и ритмичность развития («регулярно фиксировать прогресс, искать точки роста») три кита профессионального успеха.

Куда дальше? Полезные ссылки и идеи.

В этом разделе я постарался собрать полезные идеи и материалы, относящиеся к материалу кейса.

Развитие «глазами пользователя».

  • После закрытия программы данные пропадают, так как хранятся только в памяти. Смотреть в сторону встраиваемых баз данных, например, SQLLite или «взрослой» БД PostgreSQL. Полезно изучить SQL.

  • Необходимы улучшения UI — меню, popup меню, inplace edit итд. Материалов по PyQt/PySide в сети много. Какой из них лучше трудно сказать. Выбирайте сами. Как один из примеров читать тут.

  • Способ распространения приложения — скачивание из интернет с установкой python на машину пользователя. Неудобно. Можно посмотреть в сторону PyInstaller и Nuitka для сборки в исполняемый файл.

Развитие «глазами программиста».

Дорожные карты прокачки навыков

  • Хорошая статья на Хабр

  • Англоязычный roadmap

Развитие кейса

  • Популярная библиотека валидаторов Pydantic

  • Материалы по паттерну проектирования Model-View-Controller. Еще один пример, но на другом UI framework

  • MVC средствами PySide

  • Модульное тестирование приложений. библиотеки unittest и pytest, модуль pytest-qt

© Habrahabr.ru