Search
Write a publication
Pull to refresh

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

Level of difficultyEasy
Reading time4 min
Views11K

Я создаю приложение с 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._parameters = parameters
        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('Построение графиков исходных данных из файла ...')
        ...
  
if __name__ == "__main__":
    processing_params = {
            'Raw_Data': {
                'plotting_init_data': True,
                'plotting_buffers_data': False,
                'plotting_raw_data': True,
                'kwargs': {}
            }
        }
    data_processing = DataProcessing(processing_params)
    data_processing.start()

Заключение

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

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

Всем добра.

Tags:
Hubs:
+4
Comments21

Articles