Проектирование расширяемого конвейера с помощью Python
контекст: в настоящее время я использую Python для кода конвейера сокращения данных для большой астрономической системы визуализации. Основной класс конвейера передает экспериментальные данные через ряд "этапов" дискретной обработки.
этапы записываются отдельно .py файлы, которые составляют пакет. Список доступных этапов создается во время выполнения, чтобы пользователь мог выбрать, через какие этапы запускать данные. Цель этого подхода состоит в том, чтобы позволить пользователю создавать дополнительные этапы в будущем.
вопрос: все параметры конфигурации конвейера и структуры данных (в настоящее время) расположены в пределах основного класса конвейера. Есть ли простой способ получить доступ к ним из этапов, которые импортируются во время выполнения?
моя текущая лучшая попытка кажется "неправильной" и несколько примитивной, поскольку она использует циклический импорт и переменные класса. Возможно, есть способ для экземпляра конвейера передать ссылку на себя как спор с каждым из этапов, которые он вызывает?
Это мой первый раз, когда я кодирую большой проект python, и мое отсутствие знаний о дизайне действительно показывает.
любая помощь была бы весьма признательна.
4 ответов
я построила аналогичную систему, она называется collective.transmogrifier
. На днях я сделаю его более общим (в настоящее время он привязан к CMF, одной из основ Plone).
развязка
что вам нужно, это способ развязать регистрацию компонента для вашего конвейера. В Transmogrifier, я использую Зопе Компонент Architucture (воплощенные в zope.component
пакет). ZCA позволяет мне регистрировать компоненты, реализующие данный интерфейс и позже искать эти компоненты как последовательность или по имени. Есть и другие способы сделать это, например, яйца питона имеют концепцию точки входа.
дело в том, что каждый компонент в конвейере ссылается только на текстовое имя, на которое нет ссылки во время строительства. Сторонние компоненты могут быть вставлены для повторного использования путем регистрации собственных компонентов независимо от вашего конвейера пакет.
конфигурация
трубопроводы трансмогрификатора настроены с использованием текстового формата на основе python ConfigParser
модуль, где различные компоненты конвейера называются, настраиваются и прорезаются вместе. При построении конвейера каждому участку, таким образом, задается объект конфигурации. Разделы не должны искать конфигурацию централизованно, они настраиваются при создании экземпляра.
Центральный государственный
Я тоже пас в Центральном экземпляре 'transmogrifier', который представляет трубопровод. Если какой-либо компонент должен совместно использовать состояние конвейера (например, кэширование соединения с базой данных для повторного использования между компонентами), он может сделать это в этом Центральном экземпляре. Поэтому в моем случае каждая секция имеет ссылку на центральный трубопровод.
отдельные компоненты и поведение
компоненты трубопровода Transmogrifier генераторы, которые уничтожают элементы от предыдущего компонента в трубопроводе, затем дают результаты собственной обработки. Таким образом, компоненты обычно имеют ссылку на предыдущий этап, но не знают, что потребляет их выход. Я говорю "вообще", потому что в Transmogrifier некоторые элементы трубопровода могут производить элементы из внешнего источника вместо использования предыдущего элемента.
Если вам нужно изменить поведение компонента конвейера на основе отдельных обрабатываемых элементов, отметьте эти элементы дополнительной информацией для каждого компонент для обнаружения. В Transmogrifier элементы являются словарями, и вы можете добавить дополнительные ключи в словарь, который использует имя компонента, чтобы каждый компонент мог искать эту дополнительную информацию и изменять поведение по мере необходимости.
резюме
отделите компоненты конвейера с помощью косвенного поиска элементов на основе конфигурации.
когда вы создаете свои компоненты, настройте их одновременно и дайте им что они должны делать свою работу. Это может включать центральный объект для отслеживания состояния трубопровода.
при запуске конвейера, только пройти через элементы для обработки,и пусть каждый компонент базы это поведение только на этом отдельном элементе.
Ruffus - это библиотека python, "предназначенная для автоматизации научных и других анализов с минимумом суеты и наименьших усилий".
положительное: он позволяет инкрементную обработку данных, и вы можете определить очень сложные последовательности. Кроме того, задачи автоматически распараллеливаются. Он позволяет включать / выключать функции, и их порядок определяется автоматически из шаблонов, которые вы указывать.
отрицательный: иногда слишком подходящие для Python, на мой вкус, это только переключатели и заказы функций, а не, например, классы. Но тогда, конечно, у вас может быть код для инициализации классов внутри каждой функции.
для цели, которую вы хотите, вы используете @active_if идентификатор над функцией, чтобы включить или отключить его по конвейеру. Вы можете получить, будет ли он активирован из внешней конфигурации файл, который Вы читаете с ConfigParser.
чтобы загрузить значения ConfigParser, вам нужно написать другой модуль python, который инициализирует экземпляр ConfigParser. Этот модуль должен быть импортирован в первые строки модуля конвейера.
два варианта:
- имейте конфигурацию где-то еще: имейте модуль конфигурации и используйте что-то вроде системы конфигурации django, чтобы сделать это доступным.
- вместо того, чтобы этапы импортировали класс конвейера, передайте им экземпляр конвейера при создании экземпляра.
мой коллега работал над аналогичным конвейером для астрофизических карт синтетических выбросов из данных моделирования (svn checkout https://svn.gforge.hlrs.de/svn//opensesame).
то, как он это делает:
конфигурация живет в отдельном объекте (на самом деле словарь, как и в вашем случае).
этапы как:
- получить объект config при создании экземпляра в качестве аргумента конструктора
- вам конфигурация через назначение позже (например, этап.config = config_object)
- получите объект config в качестве аргумента при выполнении (например, stage.exec (config_object, other_params))