You need to be very careful when using mutable default arguments. Please see “Least Astonishment” and the Mutable Default Argument.
The reason for the difference in behaviour is that
path = path + [node1]
creates a new list and binds it to the name path
. The other 2 alternatives modify the existing list bound to path
.
As the linked question explains, default arguments are created when the function definition is compiled, not when the function is called. This is especially significant when using a mutable default arg in a recursive function because it implies that the default arg is not reset on each top-level recursive call.
If you don't want the "special" behaviour that using a mutable default arg gives you, you can do something like:
def find_path(self, node1, node2, path=None):
if path is None:
path = []
# rest of code
If None
is a valid arg for path
then you'll need to use some other sentinel, eg
sentinel = object()
def find_path(self, node1, node2, path=sentinel):
if path is sentinel:
path = []
# rest of code
Here's a short demo that illustrates the "special" behaviour of a mutable default arg. You can see that lst
remembers its previous contents.
def test(a, lst=[]):
lst += [a]
print(a, lst)
for i in range(5):
test(i)
output
0 [0]
1 [0, 1]
2 [0, 1, 2]
3 [0, 1, 2, 3]
4 [0, 1, 2, 3, 4]
In contrast, using lst = lst + [a]
, we create a new list rather than appending to the default list.
def test(a, lst=[]):
lst = lst + [a]
print(a, lst)
for i in range(5):
test(i)
output
0 [0]
1 [1]
2 [2]
3 [3]
4 [4]