Как пройти по графику зависимостей?

#python

#python

Вопрос:

У меня есть задачи, которые должны выполняться в соответствии с набором правил.

Например:

         | - File2
File1  -
        | - File3
  

Это означает, что задача File1 должна быть выполнена перед File2 и File3.
Я написал следующий код :

 import json
    json_string = """
    {
        "file1": {
            "path_to_file": "file1.txt",
            "children": "file2,file3"
        },
        "file2": {
            "path_to_file": "file2.txt",
            "children": ""
        },
        "file3": {
            "path_to_file": "a/file3.txt",
            "children": ""
    }
"""


class Node(object):
    def __init__(self, name, path_to_file=None):
        self.name = name
        self.path_to_file = path_to_file
        self.children = []

    def add_child(self, obj):
        self.children.append(obj)

    def dump(self):
        print('%s' % (self.name))
        for obj in self.children:
            obj.dump()

name2info = json.loads(json_string)

def get_tree(name):
    info = name2info[name]
    root = Node(name, info['path_to_file'])
    for child in info['children'].split(","):
        if child:
            root.add_child(get_tree(child))
    return root

root = get_tree('file1')
root.dump()
  

Что дает мне:

 file1
file2
file3
  

В этом примере print — это execution function в узле.

Проблема в том, что этот код не работает для случая, подобного:

 File1  -
        | - File3
File2  -
  

Если у меня есть:

    json_string = """
    {
        "file1": {
            "path_to_file": "file1.txt",
            "children": "file3"
        },
        "file2": {
            "path_to_file": "file2.txt",
            "children": "file3"
        },
        "file3": {
            "path_to_file": "a/file3.txt",
            "children": ""
    }
  

Это даст мне:

 file1
file3
file2
  

Это должно быть:

 file1
file2
file3   #3 is child of 1 and 2 - it can be executed only after 1 amp; 2 are done.
  

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

Комментарии:

1. Можете ли вы реструктурировать свой код так, чтобы вместо ссылок от предшественника к преемнику вы меняли отношения таким образом, чтобы узлы имели ссылки на задачи, которые должны выполняться до них?

2. Итак, это не дерево, это график. Вы не можете смоделировать это с помощью дерева.

3. Просто общий комментарий: Я думаю, что было бы лучше указывать вещи в терминах «родителей» вместо «дочерних элементов». Таким образом, при добавлении новых дочерних процессов вам нужно только коснуться одной точки для нового дочернего процесса, а не касаться всех потенциальных родителей. Вероятность того, что вы забудете обновить родительский элемент, возрастает экспоненциально по мере усложнения графика зависимостей.

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

5. @Ralf Я могу заменить дочерние элементы в Json на родительские. Но как это помогает мне?

Ответ №1:

Ваше дерево зависимостей на самом деле не дерево — это DAG. Когда вы печатаете дерево по адресу file1 , file2 оно не должно быть напечатано.

Кстати, вы не должны хранить родительский файл в json, это заставит вашу систему быть деревом. (что может быть хорошо, в зависимости от ваших требований)

Комментарии:

1. В названии написано graph 🙂 Я написал это как дерево, так как я не думал о случае, который я представил в вопросе. Очевидно, что это требует изменения структуры, я просто не знаю, как создать новый тип, чтобы я мог пройти по нему в правильном порядке выполнения. Можно предположить, что существует только один корень.

2. Ваш код должен работать нормально, не могли бы вы более конкретно рассказать о проблеме?

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

4. Ага. Вы можете использовать библиотеку. Ознакомьтесь с NetworkX: networkx.github.io

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

Ответ №2:

Алгоритм, который вам нужен, — это «топологическая сортировка»: список L элементов графика, такой, что если A предшествует B в L , то A не является потомком B . Это стандартная проблема с графом; существующие библиотеки должны справиться с ней. Вот один из них.

Обратите внимание, что в общем DAG такой порядок не всегда гарантируется.

Комментарии:

1. Ключ должен быть. Я не могу найти какую-либо компактную, хорошо документированную библиотеку.

2. Я использовал неправильный термин! Это «топологическая сортировка». Я исправил комментарий и связался с библиотекой (которую я не пробовал).

3. Я это уже нашел. не обновлялся с 2016 года, и есть 0 примеров его использования

4. Слабый. Что ж, тогда, похоже, ваши варианты — самостоятельно кодировать topo sort или использовать интерфейс внешней функции.