Взаимная рекурсия между объектами в Python

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

Вот чрезвычайно упрощенная версия моего кода:

TaskModule.py

class Task(object):
    def __init__(self, name, effect):
        self.name = name
        self.effect = effect

def execute(task):
    task.effect()

TaskTest.py

task1 = task("Do the first thing", execute(task2))
task2 = task("Do the second thing", execute(task3))
task3 = task("Do the third thing", execute(task1))

Проблема с этой реализацией состоит в том, что я ссылаюсь на task2 и task3 до того, как они были определены. Это не был бы конец света, если бы я мог исключить циклические ссылки - это был бы просто вопрос перестановки порядка, в котором создаются объекты, - но я считаю, что я должен учесть эту возможность. Я рассмотрел пару возможных обходных путей - большинство из них может потребовать от пользователя ссылки на задачи косвенно (т. Е. С помощью некоторого значения уникального идентификатора), но мне интересно, есть ли более элегантное решение для этого, которое включает в себя умную форму абстракции.

Обеспечение того, чтобы процесс создания задач / создания сети задач (как видно из TaskTest.py) был максимально простым и легким, является главным приоритетом для этого проекта, поскольку именно на это пользователи моего модуля будут тратить большую часть своей время делаю.

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

3 ответа

Решение

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

class TaskManager:
    def __init__(self, tasks=None):
        self.tasks = tasks or {}

    def register(self, name, task):
        self.tasks[name] = task

    def execute(self, name):
        self.tasks[name].effect()

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

task1 = Task("Do the first thing", [Placeholder()])
task2 = Task("Do the second thing", [execute(task1)])
task3 = Task("Do the third thing", [execute(task2)])
task1.add_dependency(task3)
task1.remove_placeholders()

Это должно быть Task("...", [Placeholder()]) скорее, чем Task("...", []) потому что последний представляет собой задачу без зависимостей, которую вы также захотите выразить.

Один из способов сделать это - создать объект TaskList для управления задачами. Реализация будет зависеть от вас, но по сути вы захотите иметь список задач, которые выполняются последовательно. Если какая-то задача производит дальнейшую работу (т. Е. Возврат, отличный от None), то эта результирующая задача будет добавлена ​​в список.

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

Другие вопросы по тегам