Search code examples
pythonrecursionpass-by-reference

List modification in recursive function calls


I have two simple recursive functions in Python as follows:

def test_list(outcome=[1]):
    outcome += [2*outcome[-1]]
    print(outcome)
    if outcome[-1] < 8:
        test_list(outcome)
    print(outcome)
def test_integer(n=1):
    n *= 2
    print(n)
    if n < 8:
        test_integer(n)
    print(n)
  • “test_integer()” takes integer “n” as input, doubles its value, prints its value, recursively calls itself, and finally prints the value of “n” again. The outcome is expectedly as follows:
    2
    4
    8
    8
    4
    2

Kowing the fact that subsequent doublings do not affect the value of the variable in the preceding recursions, I understand why the overall outcome of the function peaks at 8 and then drops to 2.

  • “test_list()” works similar to “test_integer()” but after each doubling it appends the new value of “n” to a list and then passes the list as input to the subsequent recursion. In this case, the overall outcome is different from what I expected. It seems that each time a new value is appended to the list it does affect the content of the list in the preceding recursions. More specifically, I expected the outcome to be:
[1, 2]
[1, 2, 4]
[1, 2, 4, 8]
[1, 2, 4, 8]
[1, 2, 4]
[1, 2]

But in reality, it is:

[1, 2]
[1, 2, 4]
[1, 2, 4, 8]
[1, 2, 4, 8]
[1, 2, 4, 8]
[1, 2, 4, 8]

Any hints why these two functions behave differently?


Solution

  • Normally, an augmented assignment like x += y is an alias for x = x + y, which creates a new value and binds it with the left argument.

    However, the list class redefines += in a special way, so that it directly mutates the left argument instead of assigning to it. If you want the new value semantics, you have to use the plain assignment outcome = outcome + ....

    Illustration:

    old_a = a = [1, 2]
    a += [99] # <- mutation
    print(old_a, a)  # [1, 2, 99] [1, 2, 99]
    
    old_b = b = [1, 2]
    b = b + [99] # <- new value
    print(old_b, b)  # [1, 2] [1, 2, 99]