[Tutor] Problem with python

Steven D'Aprano steve at pearwood.info
Wed Oct 20 12:13:39 CEST 2010


On Wed, 20 Oct 2010 08:02:43 am Matthew Nunes wrote:

> I cannot understand the how it claimed the code executed, and
> logically it makes no sense to me. Please explain it to me, as I
> have spent hours trying to get my head around it, as the book(in my
> opinion) did not explain it well. Here it is: 

> def factorial(n):
> if n == 0:
> return 1
> else:
> recurse = factorial(n-1)
> result = n * recurse
> return result

To be pedantic, the above code will raise a SyntaxError, because you've 
ignored or lost the indentation. Fortunately in this case it's easy to 
fix:


def factorial(n):
    if n == 0:
        return 1
    else:
        recurse = factorial(n-1)
        result = n * recurse
        return result


Let's start with an easy example: factorial(0). When Python sees 
factorial(0), it does this:

Call factorial with argument 0:
* if n == 0 <-- this is true, so execute the "if" block: 
    return 1

so the function factorial(0) returns the value 1. All that goes on 
behind the scenes. What *we* see is:

>>> factorial(0)
1

That part is easy. Now, the recursive part. What does Python do when you 
call factorial(1)?

Call factorial with argument 1:
* if n == 0 <-- this is false, so execute the "else" block: 
    recurse = factorial(n-1)

Here Python is calling a function. It happens to be the same function, 
but Python doesn't care about that, and neither should you. So Python 
simply continues:

    Call factorial with argument 1-1 = 0:

Now, we've already seen this above, but Python does the simplest thing 
that could work: it calculates the answer from scratch:

    if n == 0 <-- this is true, so execute the "if" block:
        return 1

Now Python has a value it can assign to recurse, and so it can continue:

    recurse = factorial(n-1) = factorial(0) = 1
    result = n*recurse = 1*1 = 1
    return 1

Wait a second... a couple of lines back, I said n was 1, then I said it 
was 0, and now I'm claiming it is 1 again! What gives?

The difference is that each call to factorial() gets its own local 
variable, n. When you call factorial(1), Python ends up calling the 
function twice: once with n=1, then BEFORE that calculation is 
finished, with n=0, then it falls back to the unfinished calculation.

Here's an example that might explain things better:

def factorial(n):
    print("Starting factorial calculation with n = %d" % n)
    if n == 0:
        print("Done with n = %d -- returning 1" % n)
        return 1
    else:
        print("About to call factorial again...")
        recurse = factorial(n-1)
        result = n*recurse
        print("Done with n = %d -- returning %d" % (n, result))
        return result



And here it is in action:


>>> factorial(0)
Starting factorial calculation with n = 0
Done with n = 0 -- returning 1
1
>>> factorial(1)
Starting factorial calculation with n = 1
About to call factorial again...
Starting factorial calculation with n = 0
Done with n = 0 -- returning 1
Done with n = 1 -- returning 1
1
>>>
>>> factorial(2)
Starting factorial calculation with n = 2
About to call factorial again...
Starting factorial calculation with n = 1
About to call factorial again...
Starting factorial calculation with n = 0
Done with n = 0 -- returning 1
Done with n = 1 -- returning 1
Done with n = 2 -- returning 2
2
>>>
>>>
>>> factorial(5)
Starting factorial calculation with n = 5
About to call factorial again...
Starting factorial calculation with n = 4
About to call factorial again...
Starting factorial calculation with n = 3
About to call factorial again...
Starting factorial calculation with n = 2
About to call factorial again...
Starting factorial calculation with n = 1
About to call factorial again...
Starting factorial calculation with n = 0
Done with n = 0 -- returning 1
Done with n = 1 -- returning 1
Done with n = 2 -- returning 2
Done with n = 3 -- returning 6
Done with n = 4 -- returning 24
Done with n = 5 -- returning 120
120




Notice the pattern of n values: the *first* n value that is used is the 
*last* value to be finished with.


Hope this helps!



-- 
Steven D'Aprano


More information about the Tutor mailing list