1

I'm fairly experienced with Python, but as I was helping out a friend with a script, I noticed he had solved a common problem in a different way than I was familiar with. He is making sure that the user input is in a valid format (a float); I am used to seeing something like this, which is also what all answers I could find say:

def get_input()
    while True:
        try:
            val = float(input("input pls"))
            return val
        except:
            print("not valid, try again)

He had however solved it using recursion, like this:

def get_input():
    try:
        val = float(input("input pls"))
        return val
    except:
        print("not valid, try again")
        return get_input()

Is there any downside to this?

  • 5
    Well, this might not be relevant in this specific use case, but there is a maximum recursion depth in python. Both versions suffer from a naked `except` clause, though... – juanpa.arrivillaga Feb 13 '17 at 08:25
  • 2
    Compare the two sets of code. Ask yourself if the recursive version is easier to read and support. If it isn't then keeping the stack-state is an unnecessary overhead. Recursion is only worth it when it simplifies. – cdarke Feb 13 '17 at 08:31
  • 2
    Unlike most compiled languages Python cannot optimize recursion. It also imposes a maximum recursion depth (by default 1000) to stop runaway functions from consuming all your RAM. So recursion in Python should be avoided if a simple alternative like a `while` loop can be used instead. Of course, if recursion is appropriate to the problem domain, eg dealing with recursive structures like trees, then by all means use it. – PM 2Ring Feb 13 '17 at 08:46
  • 2
    FWIW, this topic is discussed at [Asking the user for input until they give a valid response](http://stackoverflow.com/questions/23294658/asking-the-user-for-input-until-they-give-a-valid-response). – PM 2Ring Feb 13 '17 at 08:47
  • @juanpa.arrivillaga: I know there's a max depth, but don't really see how that's relevant here. Also, I'm aware of the naked `except`, I just typed up some short snippets to demonstrate the case. EDIT: Never mind, I see how it's relevant, since each input will create a new stack. @cdarke: I personally think the recursive version looks much cleaner, which is why I'm asking if there are any downsides to it, since I'm much more used to seeing the looping version. – Nicholas Tidemann Feb 13 '17 at 08:53
  • @PM2Ring: Ah, I see it there at the bottom now, didn't notice it when I read through that answer. – Nicholas Tidemann Feb 13 '17 at 08:58
  • The max depth is relevant here because if the user gives bad input ~1000 times the program will crash with `RecursionError: maximum recursion depth exceeded`. OTOH, a user who can't give correct input after that many tries probably deserves to have the program crash on them. ;) – PM 2Ring Feb 13 '17 at 10:15

2 Answers2

1

Some issues or data about this:

  1. There is a recursion limit (very hight, but may surpass)
  2. Each time the get_input method is called recursively its creating a new stack for the scope, even more, it will chain the try...except blocks, this is awful for memory usage and performance.
  3. The recursive method is usually more difficult to debug.

But since it looks like a toy example maybe this just doesnt matter.

Netwave
  • 40,134
  • 6
  • 50
  • 93
1

I personally would not do that in Python although to me it does look easy to read. The problem with python is that it does not have tail-call optimization where the approach here would be optimised by the compiler as the last thing you are doing is returning the value.

To make this work, you could use a generator based approach so that the number of times a user will get this wrong is independent to whether or not the program can handle it. (This does not look as elegant as the solution you have given but does handle the infinite recursion)

def get_input_from_keyboard():
    # I have used raw_input instead of input as I am using python 2.7
    return float(raw_input("input pls"))

def try_again():
    print("not valid, try again")
    for x in get_input():
        yield x

def get_input():
    try:
        yield get_input_from_keyboard()

    except ValueError:
        for x in try_again():
            yield x


list[get_input()][0]

That said, the recursive approach you have provided could be adapted by catching RuntimeError exception but that is very risky as the reason could be that there really is something which has gone wrong apart from a stack overflow.

Har
  • 3,727
  • 10
  • 41
  • 75