def add(a):
a = a 1
print(a)
if a < 10:
add(a)
print(a)
add(1)
I don't understand why the value of a
started reducing after 10 when it should've stopped at 10 even without an else statement. Can anyone explain the reason?
CodePudding user response:
print() debugging
With the help of some f-strings, we can sort this out:
def add(a):
a = a 1
print(f"#1, a: {a}, id: {id(a)}")
if a < 10:
print("recursion")
add(a)
print(f"#2, a: {a}, id: {id(a)}")
add(1)
# stdout:
#1, a: 2, id: 9801280
recursion
#1, a: 3, id: 9801312
recursion
#1, a: 4, id: 9801344
recursion
#1, a: 5, id: 9801376
recursion
#1, a: 6, id: 9801408
recursion
#1, a: 7, id: 9801440
recursion
#1, a: 8, id: 9801472
recursion
#1, a: 9, id: 9801504
recursion
#1, a: 10, id: 9801536
#2, a: 10, id: 9801536
#2, a: 9, id: 9801504
#2, a: 8, id: 9801472
#2, a: 7, id: 9801440
#2, a: 6, id: 9801408
#2, a: 5, id: 9801376
#2, a: 4, id: 9801344
#2, a: 3, id: 9801312
#2, a: 2, id: 9801280
Disassembler
The bytecode for your version of add()
doesn't show any BINARY_SUBTRACT
operation:
import dis
dis.dis(add)
2 0 LOAD_FAST 0 (a)
2 LOAD_CONST 1 (1)
4 BINARY_ADD
6 STORE_FAST 0 (a)
3 8 LOAD_GLOBAL 0 (print)
10 LOAD_FAST 0 (a)
12 CALL_FUNCTION 1
14 POP_TOP
4 16 LOAD_FAST 0 (a)
18 LOAD_CONST 2 (10)
20 COMPARE_OP 0 (<)
22 POP_JUMP_IF_FALSE 32
5 24 LOAD_GLOBAL 1 (add)
26 LOAD_FAST 0 (a)
28 CALL_FUNCTION 1
30 POP_TOP
6 >> 32 LOAD_GLOBAL 0 (print)
34 LOAD_FAST 0 (a)
36 CALL_FUNCTION 1
38 POP_TOP
40 LOAD_CONST 0 (None)
42 RETURN_VALUE
Explanation
The
if
statement recursively executesadd()
until before the last line of the function, which then just keeps "waiting".All these waiting "stacked" instruction lines will be eventually executed in a LIFO order, because the recent calls were called by, and solve, the old ones.
The end result is that
a
is not being decremented, instead is evaluated with the old values from each "waiting" instruction line.
Extra
This kind of recursion can be useful for writing pyramid-like (double top) patterns to the stdout:
def paint_pyramid(a):
a = a 1
print(a*'*')
if a < 5:
paint_pyramid(a)
print(a*'*')
paint_pyramid(0)
# stdout:
*
**
***
****
*****
*****
****
***
**
*