Behavior of python method in absence of return statement
Question: I have a question related to change in program behavior the absence of
_return_ statement leads to in a python method.

The _count_ method below prints number of digits in a given integer. With the
below chunk of code I get the result as 4, which is the expected result.

    
    
    def count(x,acc=0):
        if x==0:
            return acc        
        return count(x/10,acc+1)
    
    print "Count is %s" %(count(1234))
    

**Result:** _Count is 4_

If I modify the above method so that the last statement does not contain the
'return' statement the result I get is 'None'.

    
    
    def count(x,acc=0):
        if x==0:
            return acc        
        count(x/10,acc+1)
    
    print "Count is %s" %(count(1234))
    

**Result: Count is None**

(The version of Python I used is: 2.7.3)

Does the above behavior results due to the fact that Python doesn't do tail
call optimization or is there any other reasoning involved ?

A similar chunk of code in perl (which AFAIK doesn't do tail call
optimization) provides the expected result without 'return' being part of the
last statement.

    
    
    sub counter {
        my ($n,$acc) = @_;
        return $acc if ($n==0);
        counter(int($n/10), $acc+1);
    }
    print "Count is:" . counter(1234,0) ."\n"
    

**Result: Count is:4**

(The versions of Perl I ran above code chunk are : 5.14.4 and 5.8.5).

My questions are:

  * Is Tail Call optimization the reason for the behavior shown in above chunk of Python code. 
  * If that were the case then why behavior of perl code differs, which doesn't do TCO either.


Answer: Doesn't have to do with the missing of _tail optimization_ at all. Functions
in Python need the keyword `return` explicitly, otherwise is assumed their
return `None`.

I know Ruby doesn't behave that way, it returns the value of the last executed
expression. With Perl it must be the same.

It is nothing that clever, just the fact that Python programs behave that way
:)

See the disassembly for both Python functions. You may see how the one having
the `return` value actually calls the function and return the value on top of
the stack. The one that doesn't have it, see that have two instructions after
the funciont call, which load constant `None` and return it.

    
    
    def count(x,acc=0):
        if x==0:
            return acc        
        return count(x/10,acc+1)
    
    def count2(x,acc=0):
        if x==0:
            return acc        
        count(x/10,acc+1)
    
    In [7]: import dis    
    In [8]: dis.dis(count)
      2           0 LOAD_FAST                0 (x)
                  3 LOAD_CONST               1 (0)
                  6 COMPARE_OP               2 (==)
                  9 POP_JUMP_IF_FALSE       16
    
      3          12 LOAD_FAST                1 (acc)
                 15 RETURN_VALUE
    
      4     >>   16 LOAD_GLOBAL              0 (count)
                 19 LOAD_FAST                0 (x)
                 22 LOAD_CONST               2 (10)
                 25 BINARY_DIVIDE
                 26 LOAD_FAST                1 (acc)
                 29 LOAD_CONST               3 (1)
                 32 BINARY_ADD
                 33 CALL_FUNCTION            2
                 36 RETURN_VALUE
    
    In [9]: dis.dis(count2)
      2           0 LOAD_FAST                0 (x)
                  3 LOAD_CONST               1 (0)
                  6 COMPARE_OP               2 (==)
                  9 POP_JUMP_IF_FALSE       16
    
      3          12 LOAD_FAST                1 (acc)
                 15 RETURN_VALUE
    
      4     >>   16 LOAD_GLOBAL              0 (count)
                 19 LOAD_FAST                0 (x)
                 22 LOAD_CONST               2 (10)
                 25 BINARY_DIVIDE
                 26 LOAD_FAST                1 (acc)
                 29 LOAD_CONST               3 (1)
                 32 BINARY_ADD
                 33 CALL_FUNCTION            2
                 36 POP_TOP
                 37 LOAD_CONST               0 (None)
                 40 RETURN_VALUE
    

