1

I want to set up classes for the following graph structure:

  • A graph is a list of graph_nodes
  • A graph_node is an object with a property graph_data
  • A graph_data is an object with a "value" property, which can either be a primitive or a graph.

This causes circular imports since the graph_data object needs to import the graph object. Is there a better way to structure this data structure? Obviously this is a very watered down example but I think this is the key problem with my code.

Here's an example of the circular import:

#file main.py:
from graph import graph
myGraph = graph()

#file graph.py
import graph_node
class graph:
    graph_nodes = []

    def __init__(self, graph_nodes=None):
        if graph_nodes == None:
            self.graph_nodes = graph_nodes
        else:
            for i in range(5):
                self.graph_nodes.append(graph_node.graph_node())

#file graph_node.py
import graph_node_data
class graph_node:
    graph_data = None

    def __init__(self):
        self.graph_data = graph_node_data.graph_node_data()

#file graph_node_data.py
import graph
class graph_node_data:
    value = None

    def __init__(self):
        self.value = graph.graph(graph_nodes = None)

Here's a graph illustrating where this structure might be useful.

Example

8
  • 1
    why do you need to separate graph_node and graph_node_data into two classes/files ? looks like over-engineering to me. Commented Feb 22, 2017 at 17:15
  • It's not required but it's nice to keep the graph structure separated in more complicated code. Still have the circular import problem either way Commented Feb 22, 2017 at 17:17
  • In file file graph.py you import graph_node but you're not using it ;) Commented Feb 22, 2017 at 18:30
  • Yeah I simplified the code but eventually you'll need to use graph_node, for example if you need a function to process every node in the graph Commented Feb 22, 2017 at 20:08
  • 2
    You are using class attributes. Probably you want to use instance attributes. Move setting of attributes in init methods. Circular imports, if really needed, can be simulated by calling methods with module as parameter, or class, or object which class is used. E.g. graph can construct node with parameter self object, node can propagate that graph object to data, and data can use type(graph_object)() to constuct object of same type. Commented Feb 22, 2017 at 22:49

1 Answer 1

1

Just combined the code in a single file, runs fine:

class graph:
    graph_nodes = []

    def __init__(self, graph_nodes=None):
        if graph_nodes == None:
            self.graph_nodes = graph_nodes
        else:
            for i in range(5):
                self.graph_nodes.append(graph_node.graph_node())

class graph_node:
    graph_data = None

    def __init__(self):
        self.graph_data = graph_node_data.graph_node_data()

class graph_node_data:
    value = None

    def __init__(self):
        self.value = graph.graph(graph_nodes = None)

myGraph = graph()

If you really need to keep the classes in separate files, you'll need to use local imports, i.e. declare the import statement in the functions, e.g.

def fn():
    import graph
Sign up to request clarification or add additional context in comments.

1 Comment

Putting the classes in one file is a good idea but not really feasible when these classes get large. And using the import statement in the function feels hacky, maybe there's a better way to design this.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.