I'm currently working on a module that allows users to build an arbitrary task network model (for use in a discrete event simulation) by creating instances of task objects (my module provides a task class). Among other things, a task contains logic describing the effects of its completion, such as the initiation of a different task. In this way, an instance of the task class may refer to one or more other instances, with the possibility of cyclical references/mutual recursion.
Here is an extremely simplified version of my code:
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))
The problem with this implementation is that I refer to task2 and task3 before they have been defined. That wouldn't be the end of the world if I could rule out cyclical references- it would just be a question of rearranging the order in which the objects are instantiated- but I believe I should accommodate that possibility. I have considered a couple potential workarounds- most would involve requiring the user to reference tasks indirectly (i.e. by some unique identifier value)- but I wonder if there is a more elegant solution to this that involves a clever form of abstraction.
Ensuring that the process of instantiating tasks/generating the task network (as seen in TaskTest.py) is as simple and easy as possible is a top priority for this project, since that is what the users of my module will be spending most of their time doing.
I tried searching, but it seems like most questions on the topic of mutual recursion/cyclical references concern functions or classes rather than instances.
So, I think the issue here is that names and objects are being conflated. I would maybe use a structure where the task objects are organized in a dictionary and a string or an enumeration are used as keys. This way you can refer to names before they are assigned.
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()