11

I have some Python code that basically looks like this:

my_start_list = ...

def process ( my_list ):
    #do some stuff
    
    if len(my_list) > 1:
        process(my_list)
    else:
        print(my_list)
        return my_list
   
print(process(my_start_list))

The strange thing is: print(my_list) prints out the correct content. However, the second print statement printing the return value of the function always prints None. Even if I replace the normal return statement with return("abc") it is still None.

As the content of the variable seems to be correct one line before the return statement, I don't know where to start debugging. Are there any common problems that may cause this?

martineau
  • 119,623
  • 25
  • 170
  • 301
helm
  • 713
  • 2
  • 16
  • 30
  • 10
    use `return process(my_list)` when calling it recursively. – Ashwini Chaudhary Apr 03 '13 at 13:41
  • 4
    @NPE I'm assuming `#do some stuff` includes modifying the list in some way; otherwise you're right; this should recurse indefinitely. – Henry Keiter Apr 03 '13 at 13:47
  • 1
    related : http://stackoverflow.com/questions/11097822/weird-function-return-value – Ashwini Chaudhary Apr 03 '13 at 13:47
  • The do some stuff does indeed modify the list, so indefinite recursion should not occur. But the missing return was indeed the problem, I didn't know it had to be done that way. – helm Apr 03 '13 at 13:49
  • Does this answer your question? [Why does my recursive function return None?](https://stackoverflow.com/questions/17778372/why-does-my-recursive-function-return-none) – Tomerikoo Jun 21 '21 at 09:57

4 Answers4

17

Here's what happening:

  1. You call process(my_start_list).
  2. In the function, the if block is executed if len(my_list) > 1, and there are no return statement there. Now, since the else has not been executed and since that is the only place where you have the return clause, you return the default which is None.
  3. If you have 0 or 1 elements in your list, you return that list.

To fix this, you'd want to return the list returned by process(my_list).

That is:

def process(my_list):
    # do some stuff
    ...
    if len(my_list) > 1:
        return process(my_list)
    else:
        print(my_list)
        return my_list
pradyunsg
  • 18,287
  • 11
  • 43
  • 96
9

You're only returning the list when you have 1 or 0 elements in it (the base case). You need a return statement in the first block as well, where you make the recursive call, or else you drill down to the base case, return the length-1 list to the next level, and then return None the rest of the way up. So what you want looks like this, instead:

def process(my_list):
    # Do some stuff.
    if len(my_list) > 1:
        return process(my_list) #If you don't return this result, you return None
    else:
        print(my_list)
        return my_list

Now every case (not just the base case) has a return value, so that return value will propagate all the way back up to your initial call.

Henry Keiter
  • 16,863
  • 7
  • 51
  • 80
  • His function is recursive; he's going to end up with at least two return statements no matter what he does (one for the base case and one for the recursive call). – Henry Keiter Apr 03 '13 at 13:44
  • 4
    @marksweb: **Why** would a single return better? This has been discussed to death, but that is no longer true. See [Should a function have only one return statement?](http://stackoverflow.com/q/36707) – Martijn Pieters Apr 03 '13 at 13:45
  • @MartijnPieters I'm open to being wrong given my experience, but it's what I've always been told & how I work to make keeping track of things easier. I'll give that post a read! – markwalker_ Apr 03 '13 at 14:05
3

You call process recursively but never ignore it's return value when you do. Add a return statement to pass on the return value::

def process ( my_list ):
    #do some stuff

    if len(my_list) > 1:
        return process(my_list)
    else:
        print(my_list)
        return my_list

Now, when len(my_list) > 1 is True, you actually pass on the return value of the recursive call.

Martijn Pieters
  • 1,048,767
  • 296
  • 4,058
  • 3,343
2

As others have pointed out, you are missing a return statement.

I personally would turn that tail recursion into iteration:

def process(my_list):
    while True:
        # do some stuff
        if len(my_list) <= 1:
            return my_list

I think this makes the intent a little clearer, and also avoids some pitfalls associated with tail recursion.

Community
  • 1
  • 1
NPE
  • 486,780
  • 108
  • 951
  • 1,012