Python function unexpectedly memorizes the length of list and adds it to every iteration
Question:
I’m trying to check number of “stopping times” of Collatz sequence. The script appends result of every function “step” in a list which length I use to figure out overall number of “steps”. the script is ok, but! Whenever I use several times it returns wrong length of a list. I don’t know wht but it keeps previous length of a list and adds it to the next function result.
def stopping_time(n, l=[]):
if n == 1:
return
elif n % 2 == 0:
n = n // 2
l.append(n)
stopping_time(n)
else:
n = n * 3 + 1
l.append(n)
stopping_time(n)
return len(l)
a = stopping_time(4)
b = stopping_time(13)
c = stopping_time(19)
d = stopping_time(27)
print(a, b, c, d)
# 'a' shoulb be 2, 'b' should be 9, 'c' should be 20 and 'd' should be 111
# instead its 2, 11, 31, 142
Any thoughts?
Answers:
It’s not a bug, but a feature. Python indeed memorizes mutable values passed as default arguments between calls (cf. https://docs.quantifiedcode.com/python-anti-patterns/correctness/mutable_default_value_as_argument.html).
The solution may be passing the sentinel value as an argument, something like:
def foo(bar=None):
if bar is None:
bar=[]
...
In general, you set your list argument to None by default and then set it inside the function to [] if no other value was passed by the caller.
I’m trying to check number of “stopping times” of Collatz sequence. The script appends result of every function “step” in a list which length I use to figure out overall number of “steps”. the script is ok, but! Whenever I use several times it returns wrong length of a list. I don’t know wht but it keeps previous length of a list and adds it to the next function result.
def stopping_time(n, l=[]):
if n == 1:
return
elif n % 2 == 0:
n = n // 2
l.append(n)
stopping_time(n)
else:
n = n * 3 + 1
l.append(n)
stopping_time(n)
return len(l)
a = stopping_time(4)
b = stopping_time(13)
c = stopping_time(19)
d = stopping_time(27)
print(a, b, c, d)
# 'a' shoulb be 2, 'b' should be 9, 'c' should be 20 and 'd' should be 111
# instead its 2, 11, 31, 142
Any thoughts?
It’s not a bug, but a feature. Python indeed memorizes mutable values passed as default arguments between calls (cf. https://docs.quantifiedcode.com/python-anti-patterns/correctness/mutable_default_value_as_argument.html).
The solution may be passing the sentinel value as an argument, something like:
def foo(bar=None):
if bar is None:
bar=[]
...
In general, you set your list argument to None by default and then set it inside the function to [] if no other value was passed by the caller.