简体   繁体   中英

Recursive stacks frames have identical values in visual studio debugger for python

I was trying to do a depth first recursive search in python on a graph class I made. I failed the test at the assertion level for some unknown reason: AssertionError: None not found in [[1, 2, 4, 6], [1, 2, 4, 7, 6]] , I understand the meaning of that error and I wasn't going to ask about the algorithm directly but about something else I noticed.

Using VS Code python debugger, I decided to inspect my code in operation and I notice as new stackframes got pushed to the stack with each new call (see image), they shared the same values for the 'visited' (set) and 'path' (list) instances (completely unexpected and confusing). I added a callnumber dummy variable as a sanity check to ensure I was using the debugger correctly, and I was.

I don't know why I didn't catch it earlier, especially since I've been doing a lot with C and memory management. These objects are obviously being put on the heap and passed by reference, and therefore not really separate instances, right?

Is there any way to make these stack based variables? I know that's not recommended as there's a risk of stack overflow (no pun intended) and not a good use of space.

Now that I understand what's going on, I was wondering if you had some pointers (pun intended this time) on how I make this algorithm go, given that this approach doesn't work because it's heap based? I don't need it solved, just some tips or clues, stuff that generates deeper thought.

Btw I'm always trying to get better at reading and understanding documentation. I like the docs to be my first go to actually, but when I google something for python, they're always close to the bottom past random tutorials unlike for other languages. I went to the official page on built in types just now and searched for words like heap or memory for list and dict and nowhere did I find it explained where these objects live, how they grow or are resized, etc, although it's probably so obvious and implicit for experienced programmers but anyway here is my code

class Graph:
    def __init__(self):
        self.vertices = {}
    def add_vertex(self, vertex_id):
        self.vertices[vertex_id] = set()
    def add_edge(self, v1, v2):
        self.vertices[v1].add(v2)
    # more functions

    def dfs_recursive(self, starting_vertex, destination_vertex, visited = set(), path=[]):
        """
        Return a list containing a path from
        starting_vertex to destination_vertex in
        depth-first order.

        This should be done using recursion.
        """
        result = None
        path.append(starting_vertex)
        visited.add(starting_vertex)
        if starting_vertex == destination_vertex:
            return path
        for neighbor in self.vertices[starting_vertex]:
            if neighbor not in visited:
                visited.add(neighbor)
                result = self.dfs_recursive(neighbor, destination_vertex, visited, path, callnumber+1)
        return result

在此处输入图像描述

Edit: Solved the algorithm thanks to input of rioV8. Here is my code

    def dfs_recursive(self, starting_vertex, destination_vertex, visited = None, path=None, callnumber=1):

        path = path or []
        visited = visited or set()
        path.append(starting_vertex)
        visited.add(starting_vertex)
        if starting_vertex == destination_vertex:
            return path
        for neighbor in self.vertices[starting_vertex]:
            if neighbor not in visited:
                result = self.dfs_recursive(neighbor, destination_vertex, visited.copy(), path[:], callnumber+1)
                if result is not None and result[-1] == destination_vertex:
                    return result
        return None

Don't use modifiable objects as function default arguments.

They are created at function compilation and not at function call. And are reused for every call.

Use None and create the modifiable inside the function.

And make a copy of the modifiable at the recursive call.

def dfs_recursive(self, starting_vertex, destination_vertex, visited=None, path=None):
    visited = visited or set()
    path = path or []
    # rest of function
    result = self.dfs_recursive(neighbor, destination_vertex, visited.copy(), path[:], callnumber+1)

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM