3

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.
sateesh
  • 27,947
  • 7
  • 36
  • 45
  • Also see http://stackoverflow.com/questions/17778372/why-does-my-python-function-return-none – devnull Feb 26 '14 at 14:56
  • Also http://stackoverflow.com/questions/15788969/python-function-always-returns-none – devnull Feb 26 '14 at 14:56
  • 1
    A better analogy might be that if Python reaches the end of a function without an explicit return statement being made, it is as if you had "return" without an argument. Thus, you are returning but with no return value specified (return None). – sabbahillel Feb 26 '14 at 15:14

4 Answers4

6

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
Paulo Bu
  • 29,294
  • 6
  • 74
  • 73
  • 2
    I would tell the OP that the absence of a return statement is similar to "return" without an argument. That may make it easier to see the result by making the analogy to other languages. – sabbahillel Feb 26 '14 at 15:10
  • @sabbahillel It's a good way to see it too, thanks for pointing out! – Paulo Bu Feb 26 '14 at 15:13
3

Without the return, the only case where your return something if when acc == 0; for any other call to your count recursive method, you don't return anything, hence the None received.

Unlike other languages (scala, apparently perl as well, probably others), python does not return the last statement by default, you have to call return explicitely.

Gorkk
  • 1,047
  • 11
  • 25
2

Tail call elimination does not prevent Perl's semantics (though without Perl semantics, you'd have far fewer tail calls since every function would implicitly end with return;). It's purely a language design choice. The reason Perl and Python differ is that they weren't designed by the same people.

The syntax for returning a value varies by language simply due to choice. The designers of Python opted to require an explicit return statement. In QBasic, you had to assign the value to return to a variable that shared the same name as the function.

ikegami
  • 367,544
  • 15
  • 269
  • 518
0

Functions in Python without a return statement return the value None.

In this case, you are only returning a value when x == 0, therefore the None that you get. Check the Python tutorial for more info: http://docs.python.org/2/tutorial/controlflow.html#defining-functions

Alvaro
  • 2,227
  • 1
  • 14
  • 11