def dfs_func(graph, start, to_visit=None):
    # Initialize all the nodes our algorithm has to visit into a set
    if to_visit is None:
        to_visit = set()
    
    # Add the starting point to our set
    to_visit.add(start)

    # Print our starting point
    print(start)

    # Find the next node our algorithm has to visit
    for next_node in graph:
        # Error handling for subtraction between sets and strings
        if type(next_node) == set:
            # Remove the first element of the set (as we already visited that) to figure the next node to move to
            for next in graph[start] - str(to_visit):
                # Loop the process as long as all the nodes are reached
                dfs_func(graph, next, to_visit)
        # If next_node isn't a set and is a string (the only other type)
        else:
            # Remove the first element of the set (as we already visited that) to figure the next node to move to, but first graph[start] to a set
            for next in set(graph[start]) - to_visit:
                # Loop the process as long as all the nodes are reached
                dfs_func(graph, next, to_visit)
    # Return the process so we can print it to the console                
    return to_visit

"""
Our graph:
         0
        / \
       1   2
     / | \  \
    0  3  4  0
      / / \
     1  2   3
"""
graph = {'0': set(['1', '2']),
         '1': set(['0', '3', '4']),
         '2': '0',
         '3': '1',
         '4': set(['2', '3'])}

# FInally, call the function
dfs_func(graph, '0')