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

Шаблон проектирования рабочего процесса Python

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

Моя ситуация такова: я разрабатываю научную утилиту с интерактивным интерфейсом. Пользовательский ввод должен инициировать визуальную обратную связь (duh), некоторые из них непосредственно, то есть редактирование геометрии домена и некоторые из них как можно скорее, без блокировки взаимодействия с пользователем, скажем, при решении некоторых PDE по указанному домену.

Если я вычеркиваю диаграмму всех операций, которые мне нужно выполнить, я получаю этот довольно ужасный граф, раскрывая все возможности для parallelism и кеширования/повторного использования частичных результатов. Так что я хочу, прежде всего, использовать этот parallelism прозрачным способом (выбранные подзадачи, выполняемые в отдельных процессах, результаты изначально "присоединяются" к нисходящим задачам, ожидая, что все их входы будут готовы), плюс нужно только пересчитать эти входные данные ветки, которые на самом деле имеют свой вход, изменились

pyutilib.workflow, похоже, ближе всего к тому, что я ищу, за исключением того, что это не так (для начала не требуется никаких подпроцессов). Это кажется довольно разочаровывающим; в то время как я не инженер-программист, я говорю, что я не прошу ничего сумасшедшего здесь.

Другим осложняющим фактором является сложная интеграция с пользовательским интерфейсом, которую я желаю, какие другие решения для научного рабочего процесса, похоже, не предназначены для обработки. Например, я хотел бы передать событие перетаскивания через преобразование node для дальнейшей обработки. Преобразование node имеет два входа; входной порт состояния аффинного преобразования и класс точек, который знает, что с ним делать. Если входной порт аффинного преобразования "грязный" (ожидание обновления его зависимостей), событие следует удерживать до тех пор, пока оно не станет доступным. Но когда событие прошло node, порт eventinput должен быть помечен как обработанный, поэтому он не отказывается, когда аффинное преобразование изменяется из-за дальнейшего ввода пользователем. Это всего лишь пример одной из многих проблем, которые возникают из-за того, что я не вижу, чтобы меня обращали куда угодно. Или, что делать, когда длинная ветвь для соединения с вилкой получает новый вход, когда она находится в середине хрустания предыдущего ввода.

Итак, мой вопрос: знаете ли вы, что знаете хорошие книги/статьи о шаблонах проектирования рабочего процесса, которые я должен прочитать? Или я пытаюсь поместить квадратную привязку в круглое отверстие, и вы знаете совершенно другую модель дизайна, о которой я должен знать? Или пакет python, который делает то, что я хочу, независимо от того, что он надет на одежде?

Ive покатался по собственному решению на вершине enthought.traits, но я не совсем этому доволен, так как это похоже на грубое и дрянное переосмысление колеса. Кроме того, что я не могу найти какие-либо колеса в любом месте в Интернете.

ПРИМЕЧАНИЕ. Я не ищу webframeworks, графических дизайнеров рабочих процессов или каких-либо специальных инструментов. Просто что-то концептуально, как pyutilib.workflow, но включая документацию и набор функций, с которыми я могу работать.

# # # EDIT: здесь я нахожусь после более глубокого чтения и размышлений по этому вопросу: # # #

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

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

То, что я ищу (и начал писать сам, это будет довольно круто), будет выглядеть так: на его основе представлен общий язык-декларация с использованием агностического описания рабочего процесса, основанный на декораторах и некоторых мета -magic, чтобы преобразовать выражение, подобное приведенному ниже, в объявление рабочего процесса, содержащее всю необходимую информацию:

@composite_task(inputs(x=Int), outputs(z=Float))
class mycompositetask:
    @task(inputs(x=Int), outputs(y=Float))
    def mytask1(x):
        return outputs( y = x*2 )
    @task(inputs(x=Int, y=Float), outputs(z=Float))
    def mytask2(x, y):
        return outputs( z = x+y )
    mytask1.y = mytask2.y   #redundant, but for illustration; inputs/outputs matching in name and metadata autoconnect

Возвращение декораторов - класс декларации задачи /compositetask/workflow. Вместо просто ограничений типов другие метаданные, необходимые для типа рабочего процесса, легко добавляются к синтаксису.

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

В своем простейшем воплощении wed имеет что-то вроде:

wf   = workflow_factory(mycompositetask)
wf.z = lambda result: print result   #register callback on z-output socket
wf.x = 1    #feed data into x input-socket

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

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

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

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

4b9b3361

Ответ 1

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

Как есть слон?

Уровень A: разработка программного обеспечения

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

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

  • Schecule выполните некоторую работу - команда должна быть поставлена ​​в очередь на интеллектуальный уровень, чтобы он мог получать информацию, когда он добирается до нее.
  • Прочитать результаты - результаты должны быть поставлены в очередь на интеллектуальном уровне, чтобы их можно было просто "выскочить" и отобразить.
  • Отмена/останов/выход - просто поднимите флаг. Смарт-слой должен время от времени проверять этот флаг.

Не слишком беспокоитесь о том, что некоторые пользовательские операции реагируют слишком медленно - если у вас есть сплошное ядро ​​проектирования, вы можете позже настроить приоритеты ввода пользователя. Или добавьте кратковременное часовое стекло или подобное. Или даже отменить все длительные операции, которые устаревают после ввода определенного пользователя.

Уровень B: интеллектуальный уровень тяжелой атлетики

Нет "лучшей" основы для "любой" тяжелой работы.

Итак, я предлагаю вам как можно проще разработать кормление (по пользовательскому интерфейсу) этого уровня без каких-либо фреймворков.

Внутри вы можете реализовать его с помощью некоторых фреймворков, но в будущем у вас будет возможность переработать трудоемкие элементы по мере необходимости. Например, в будущем вы могли бы:

  • дать некоторую математику GPU
  • обмениваться задачами с серверами-фермами
  • включить облачные вычисления

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

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

ИЗМЕНИТЬ в ответ на редактирование

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

Подсказка состоит в том, что вы предложили наиболее общую задачу, которую должны определять Int и Float. Это может сделать вас счастливым на сегодня, но завтра это закончится неудачно. Точно как блокировка в супер-абстрактный фреймворк.

Путь правильный - иметь тяжелую работу "задача" в вашем дизайне. Но он не должен определять Int или Float. Сосредоточьтесь на вышеупомянутых "пусках", "прочитайте" и "остановитесь" вместо этого. Если вы не видите размер слона, который вы едите, вы можете его не есть и в конечном итоге голодать:)

С уровня A - проектная перспектива - вы можете определить задачу, чтобы содержать что-то вроде этого:

class AnySuperPowerfulTask:
    def run():
        scheduleForAThreadToRunMe()
    def cancel():
        doTheSmartCancellationSoNobodyWouldCrash()

Это дает вам основание - нейтральное, но чистое и развязанное с точки зрения уровня A (desing).

Однако вам понадобится какая-то настройка задачи и получение реального результата, не так ли? Конечно, это попало бы в Уровень B мышления. Это будет специфично для задачи (или для группы задач, выполняемых как промежуточная база). Конечной задачей может быть следующее:

class CalculatePossibilitiesToSaveAllThePandas(SuperPowerfulTask):
    def __init__(someInt, someFloat, anotherURL, configPath):
        anythingSpecificToThisKindOfTasks()
    def getResults():
        return partiallyCalculated4DimensionalEvolutionGraphOfPandasInOptimisticEnvoronment()

(Образцы преднамеренно неверны для python, чтобы сосредоточиться на дизайне, а не на синтаксисе).

Уровень C - абстракция-нирвана

Похоже, этот уровень следует упомянуть в этом посте.

Да, есть такая ошибка, которую могут подтвердить многие хорошие дизайнеры. Состояние, в котором вы могли бы бесконечно (и без каких-либо результатов) искать "общее решение", то есть серебряную пулю). Я предлагаю вам заглянуть в это, а затем быстро выскочить, прежде чем слишком поздно;) Впадать в эту ловушку не стыдно - это нормальный этап развития величайших дизайнеров. По крайней мере, я пытаюсь верить так:)

EDIT 2

Вы сказали: "Я работаю над частью программного обеспечения, и im stuckмежду тем, что я не знаю, что я делаю, и чувствую, что я изобретаю колесо".

Любой разработчик программного обеспечения может застрять. Возможно, следующий уровень мышления может помочь вам. Вот оно:

Уровень D - Я застрял

Предложение. Оставьте здание. Пройдите в соседний кафе, закажите лучший кофе и сядьте. Задайте себе вопрос: "Что мне нужно?". Обратите внимание, что он отличается от вопроса "Чего я хочу?". Пинг до тех пор, пока вы не устраните неправильные ответы и не начнете следить за правильными:

Неверные ответы:

  • Мне нужна структура, которая будет делать X, Y и Z.
  • Мне нужна отвертка, которая может работать 200 миль в час и собирать лес на ферме поблизости.
  • Мне нужна потрясающая внутренняя структура, которую мой пользователь никогда не увидит.

Правильные ответы (простите меня, если я понял вашу проблему неправильно):

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