Подтвердить что ты не робот

Проектирование расширяемого конвейера с Python

Контекст: В настоящее время я использую Python для кода для сокращения данных для большой астрономической системы визуализации. Основной класс трубопроводов передает экспериментальные данные через несколько ступеней дискретной обработки.

Этапы записываются в отдельные файлы .py, которые составляют пакет. Список доступных этапов генерируется во время выполнения, поэтому пользователь может выбирать, на каких этапах запускать данные. Цель этого подхода - дать пользователю возможность создавать дополнительные этапы в будущем.

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

Моя текущая лучшая попытка кажется "неправильной" и несколько примитивной, поскольку она использует циклический импорт и переменные класса. Возможно ли, чтобы экземпляр конвейера передал ссылку на себя в качестве аргумента для каждого из этапов, которые он вызывает?

Это мой первый раз, когда я кодировал большой проект python, и мои недостатки в дизайне действительно демонстрируются.

Любая помощь будет принята с благодарностью.

4b9b3361

Ответ 1

Я построил подобную систему; он называется collective.transmogrifier. На днях я сделаю его более общим (теперь он привязан к CMF, одному из оснований Plone).

Развязка

Что вам нужно, это способ отделить регистрацию компонента для вашего конвейера. В Transmogrifier я использую Zope Component Architucture (воплощен в zope.component). ZCA позволяет мне регистрировать компоненты, которые реализуют данный интерфейс, а затем искать эти компоненты как последовательность или по имени. Существуют и другие способы сделать это, например, яйца python имеют концепцию точки входа.

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

Конфигурация

Конвейеры Transmogrifier сконфигурированы с использованием текстового формата, основанного на python ConfigParser module, где указаны различные компоненты конвейера, настроены, и слот вместе. При конструировании конвейера каждому разделу присваивается объект конфигурации. Разделы не должны искать конфигурацию централизованно, они настроены на создание экземпляра.

Центральное состояние

Я также перехожу в центральный экземпляр "transformogrifier", который представляет конвейер. Если какой-либо компонент должен совместно использовать состояние одного конвейера (например, кэширование соединения с базой данных для повторного использования между компонентами), они могут сделать это в этом центральном экземпляре. Поэтому в моем случае каждый раздел имеет ссылку на центральный трубопровод.

Индивидуальные компоненты и поведение

Компоненты конвейера Transmogrifier являются генераторами, которые потребляют элементы из предыдущего компонента в конвейере, а затем дают результаты своей собственной обработки. Компоненты, как правило, имеют ссылку на предыдущий этап, но не знают, что потребляет их выход. Я говорю "вообще", потому что в Transmogrifier некоторые элементы конвейера могут создавать элементы из внешнего источника вместо использования предыдущего элемента.

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

Резюме

  • Разделите компоненты конвейера, используя косвенный поиск элементов на основе конфигурации.

  • Когда вы создаете экземпляры своих компонентов, настройте их одновременно и дайте им то, что им нужно, чтобы выполнить свою работу. Это может включать в себя центральный объект для отслеживания состояния, специфичного для конвейера.

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

Ответ 2

Ruffus - это библиотека python, "предназначенная для автоматизации научных и других анализов с минимальной суетой и наименьшими усилиями".

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

Отрицательный: он иногда слишком питоничен для моего вкуса, он только переключает и заказывает функции, а не, например, классы. Но тогда, конечно, вы можете иметь код для инициализации классов внутри каждой функции.

Для этой цели вы используете идентификатор @active_if над функцией, чтобы включить его или отключить по конвейеру. Вы можете получить информацию о том, будет ли он активирован из внешнего файла конфигурации, который вы читаете с помощью ConfigParser.

Чтобы загрузить значения ConfigParser, вам нужно написать еще один модуль python, который инициализирует экземпляр ConfigParser. Этот модуль должен быть импортирован в первые строки модуля конвейера.

Ответ 3

Два варианта:

  • Конфигурация где-то еще: иметь конфигурационный модуль и использовать что-то вроде системы конфигурации django, чтобы сделать это доступным.
  • Вместо того, чтобы этапы импортировали класс конвейера, передайте им экземпляр конвейера при создании экземпляра.

Ответ 4

Мой коллега работал над аналогичным конвейером для астрофизических карт синтетических выбросов из данных моделирования (svn checkout https://svn.gforge.hlrs.de/svn//opensesame).

Как он это делает:

Конфигурация живет в отдельном объекте (фактически в словаре как в вашем случае).

Этапы:

  • получает объект конфигурации при создании экземпляра как аргумент конструктора
  • получить конфигурацию через назначение позже (например, stage.config = config_object)
  • получает конфигурационный объект в качестве аргумента при выполнении (например, stage.exec(config_object, other_params))