Как я заменил кучу флагов двумя словарями в Python

от автора

Я создаю приложение с GUI для сбора и обработки данных с микроконтроллера на Python с помощью PyQt. И вот я наконец-то доделал часть функционала, предназначенного для взаимодействия компьютера с платой STM32, теперь необходимо было сделать интерфейс для обработки данных, в котором легко можно было бы настраивать параметры выполнения программы. Я начал думать, как не вносить в программу кучу флагов с соответствующими if-else конструкциями, и вот, что я придумал.

Будь как Дрейк при написании кода

Будь как Дрейк при написании кода

Ещё немного контекста

Если кратко, то моя программа строит перемещение железнодорожной телеги с платой по данным гироскопа и акселерометра, этакая автономная система навигации. Отдельно собираются данные, затем запускается их обработка. В результате, пользователь получает необходимые графики и файлы с данными в удобном виде. Нюанс заключался в том, что я хотел предоставить пользователю возможность самому решать, какие результаты ему нужны, а какие нет. Например, в один раз пользователю необходимы и графики исходных данных, и графики отфильтрованных данных с помощью фильтра Калмана (теория алгоритма хорошо описана тут, а практическое применение тут). А в другой раз ему не нужны графики исходных данных. Для этого он активирует или деактивирует соответствующий checkBox в GUI. И вот как это реализовал.

Реализация

Для начала нужно состояния всех чекбоксов сохранить в один словарь любым способом. Получаем такой словарь:

processing_params = {         'Raw_Data': {             'plotting_init_data': True,             'plotting_buffers_data': False,             'plotting_raw_data': True,             'kwargs': {}         }     }

Во вложенный словарь ‘kwargs’ можно записать необходимые параметры, которые будут принимать методы. В данном случае, оставим его пустым.

Всю последовательность шагов обработки данных я реализовал в отдельном классе DataProcessing, в котором содержатся как и методы обработки данных, так и необходимые поля, в которых хранятся необходимые данные.

Вот основные методы для визуализации собранных данных:

def _plotting_init_data(self):     print('Построение графиков данных выставки из файла ...')     ...  def _plotting_buffers_data(self):     print('Построение графиков буферных данных из файла ...')     ...  def _plotting_raw_data(self):     print('Построение графиков исходных данных из файла ...')     ...

А теперь вот моё ноухау (ну или очередной велосипед). В класса DataProsessing мы создаём второй словарь, ключи в котором полностью совпадают с ключами первого словаря, а в значения записываем сами методы, отвечающие за тот или иной функционал. Продолжая пример, создаём такой словарь:

self._config = {     'Raw_Data': {         'plotting_raw_data': self._plotting_raw_data,         'plotting_init_data': self._plotting_init_data,         'plotting_buffers_data': self._plotting_buffers_data     } }

И теперь мы можем запустить выполнение всех выбранных функций одним циклом!

# ------------------------------- # Визуализация исходных данных # ------------------------------- def _raw_data_plotting(self):     print('# -------------------------------')     print('# Визуализация исходных данных')     print('# -------------------------------')     class_params: dir = self._config['Raw_Data']     user_params: dir = self._parameters['Raw_Data']      for key, value in user_params.items():         if value:             class_params[key](**user_params['kwargs'])

А благодаря тому, что Python позволяет передавать в функции неопределённое количество именованных переменных, функции могут иметь абсолютно разные входные параметры (что лучше избегать в данном контексте, но возможность такая есть).

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

# ------------------------------- # Визуализация исходных данных # ------------------------------- Построение графиков данных выставки из файла ... Построение данных из файла ...
Представленный код класса DataProsessing целиком:
class DataProcessing:     def __init__(self, parameters: dict):         self._received_data = {}         self._config = {             'Raw_Data': {                 'plotting_raw_data': self._plotting_raw_data,                 'plotting_init_data': self._plotting_init_data,                 'plotting_buffers_data': self._plotting_buffers_data             }         }      # ---------------------------------     # Основная функция обработки данных     # ---------------------------------     def start(self):         self._decoding()         self._file_classification()         self._raw_data_plotting()         self._raw_data_analysis()      # -------------------------------     # Чтение данных из файлов     # -------------------------------     def _decoding(self):         ...              # -------------------------------     # Классификация файлов     # -------------------------------     def _file_classification(self):         ...      # -------------------------------     # Визуализация исходных данных     # -------------------------------     def _raw_data_plotting(self):         print('# -------------------------------')         print('# Визуализация исходных данных')         print('# -------------------------------')         class_params: dir = self._config['Raw_Data']         user_params: dir = self._parameters['Raw_Data']          for key, value in user_params.items():             if value:                 class_params[key](**user_params['kwargs'])                      def _plotting_init_data(self):         print('Построение графиков данных выставки из файла ...')         ...          def _plotting_buffers_data(self):         print('Построение графиков буферных данных из файла ...')         ...          def _plotting_raw_data(self):         print('Построение графиков исходных данных из файла ...')         ...

Заключение

Благодаря такой реализации, программу легко дополнять необходимым функционалом. Пользователю необходимо нажать буквально пару кнопок для детальной настройки результатов работы программы, а Вам, как разработчику, добавить несколько полей в нужные словари.

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

Всем добра.


ссылка на оригинал статьи https://habr.com/ru/articles/934314/