Передача объектов Python между задачами в Луиджи?

Я кодировал свой первый проект в Python 3.6, используя Spotify Luigi, чтобы организовать некоторые задачи обработки естественного языка в конвейере.

Я заметил, что функция output() класса Task всегда возвращает какой-то объект Target, который находится где-то в каком-то файле, будь то локальным или удаленным. Поскольку мои Задачи создают более сложные структуры данных, такие как деревья синтаксического анализа, мне очень сложно записывать их в файлы в виде строк и читать их снова после.

Поэтому я хотел бы спросить, есть ли возможность пропускать объекты Python между задачами внутри конвейера?

Ответ 1

Короткий ответ: No.

Параметры Luigi ограничены объектами date/datetime, строкой, int и float. См. документы для справки.

Это означает, что вам нужно сериализовать сложную структуру данных как строку (используя json, msgpack, любой нужный сериализатор и даже сжать его) и передать его как строковый параметр.

Конечно, вы можете написать собственный подкласс Parameter, но вам нужно будет в принципе реализовать методы сериализации и разбора.

Но учтите: если вы используете параметры вместо сохранения ваших вычисленных данных в цель, вы потеряете одно ключевое преимущество использования Luigi: если родительская задача в дереве терпит неудачу больше, чем количество повторений, которые вы указываете, то вам нужно будет запустить задачу, которая снова вычисляет эту сложную структуру данных. Если ваши задачи вычисляют сложные данные или занимают значительное количество времени или потребляют много ресурсов, тогда вы должны сохранить вывод в качестве цели, чтобы снова не выполнять все эти дорогостоящие вычисления.

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

Также обратите внимание, что цели - это не только файлы: вы можете сохранить свои данные в таблице базы данных, Redis, Hadoop, индекс эластичного поиска и многое другое: http://luigi.readthedocs.io/en/stable/api/luigi.contrib.html#submodules

Ответ 2

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

В luigi.mock есть специальная цель luigi.mock которая позволяет хранить его "файл" в памяти.

API похож на другие классы наследования Target, поэтому вам придется open, read и write в него. Внезапно он поддерживает только string ввод, поэтому вам все равно нужно сериализовать ваш объект (что связано с отправкой этих данных по конвейеру между процессами). Смотрите следующий пример (сериализация yaml):

import yaml
from luigi import Task

class TaskA(Task):
    def output(self):
        return MockFile('whatever')

    def run(self):
        object_to_send = yaml.dump({"example": "dict"})

        _out = self.output().open('r')
        _out.write(object_to_send)
        _out.close()


class TaskB(Task):
    def requires(self):
        return TaskA()

    def run(self):
        _in = self.input().read('r')
        serialised = _in.read()
        deserialised = yaml.load(serialised)
        print(deserialised)

Имейте в виду, что сериализация больших объектов может занять много времени.