Is this recursive function an acceptable use-case for mutable default arguments?

Viewed 96

I have a function that recursively finds a solution to a maze represented by a 2D array. I declared all the default arguments in a way that would allow me to call this function from main without passing any arguments. One of these default arguments is an empty list, which I understand is bad practice. I'm wondering if this is a valid use-case of a mutable default argument, or if there's a better way to do this:

def solve_maze(self, i=0, j=0, path=[]):
    if (i, j) in self.memory:
        return False
    if self.maze[i][j] == 'D':
        return path[:-1]
    if not (directions := self.get_directions(i, j, [[i, j]] + path)):
        self.memory.append((i, j))
        return False
    move = {'l': [i, j-1], 'u': [i-1, j], 'r': [i, j+1], 'd': [i+1, j]}
    for k in range(len(directions)):
        best_dir = directions[k]
        if solution := self.solve_maze(move[best_dir][0], move[best_dir][1], [[i, j]] + path):
            return solution
    self.memory.append((i, j))
    return False

This function behaves exactly how I need it, so I don't need help making it work. Using this mutable default is not causing me issues. This is simply a personal curiosity on "proper Python coding".

I do realize the obvious solution is something akin to this:

def solve_maze(self, i=0, j=0, path=None):
    if not path:
        path = []

But this just adds clutter to my code. I'm curious what the "correct" way to do this is.

1 Answers

I'd say the problem isn't that you're using a mutable object there, but that you're not using it... as such. You grow your path with [[i, j]] + path. To build a path of length n, that takes Θ(n²) time and space. Would be more efficient to actually modify the list, i.e., append to and pop from it. Preferably at the end instead of the start like you do now. If you do need the result in reverse, you could still reverse the complete path once you found it.

Related