While loop vs recursion differences in try, except, finally error handling

82 views Asked by At

What are the differences between using a while loop vs recursion in try, except, finally error handling?

When I use a while loop in user input error handling:

while True:
    try:
        user_num = float(input("Enter a number: ")
        break
    except ValueError as e:
        print("That is not a number. Error:", e)
    finally:
        print("Cycle is done.")

The code loops until there are no errors and each loop prints "Cycle is done."

When I use recursion instead of a while loop:

try:
    user_num = float(input("Enter a number: ")
except ValueError as e:
    print("That is not a number. Error:", e)
    get_num()
finally:
    print("Cycle is done.")

The code loops until there is no error as well and after a success it prints "Cycle is done." * n where n = number of cycles.

Initially I expected them both to run the same. It seems that the while loop will hit the finally section before looping, and that the recursion method is still executing the except code so after a success, every call reaches finally in LIFO order.

I read that the recursion method will eventually raise a RecursionError: maximum recursion depth exceeded error if cycled long enough.

Are there other differences between these two methods of looping error handling?

3

There are 3 answers

1
patjcolon On

As commenters pointed out, this question could be answered with an understanding of the call stack and iteration vs recursion. I will answer my question with relevant key highlights from my research on these topics:

Iteration within a function will not add to the call stack, but recursive calls within a function will add to the call stack. Each recursion adds a stack frame to the call stack which takes memory. It's worth mentioning that Python is not designed to optimize tail recursion so as to maintain an accurate traceback. Python raises a RecursionError to prevent stack overflow when the recursion limit of 1000 is reached. Therefore, iteration, even when having the same Big O notation, is faster and more space efficient. Recursion can pass information to a child function call using parameters and it can pass information back to the parent calling function using return values. This makes performing operations on trees and graphs easier.

While there is no one size fits all answer, in most cases iteration is the preferable choice, especially when the goal is to repeat the code until a certain condition becomes False, like in my example. However, recursion is useful for navigating non-linear data structures and in situations when a problem can be broken down into smaller pieces.

0
anom907 zat On

The finally codeblock only execute after the except has completed. But look at both codes with a dummy returnToStart method.

When you do a while, python run something like that:

try:
    user_num = float(input("Enter a number: ")
    break
except ValueError as e:
    print("That is not a number. Error:", e)
finally:
    print("Cycle is done.")
if notBreaked():
    returnToStart()

The second code will do this:

try:
    user_num = float(input("Enter a number: ")
    break
except ValueError as e:
    print("That is not a number. Error:", e)
    returnToStart()
finally:
    print("Cycle is done.")

So the except codeblock won't terminate until every call to the function was made. Thus all of the finally will only occure after each get_num() call has terminated the except codeblock

0
tdelaney On

Recursion is most useful when you are storing intermediate values. In a language like C where a stack frame and function call are lightweight, recursion makes sense. You have to be careful, though, because all data in the intermediate stack frames remain in memory until the recursion finally rewinds. But it sill compares well to an expanding list holding intermediate data structures allocated from the heap.

Python is different. Python's "stack frame" is really a list of function objects - one created for each function call. It's no better than a list storing intermediate values in a while loop. As such, there isn't any benefit to recursion. That's a strong statement and maybe there is some use, but usually not.

In your case, you don't care about the intermediate wrong answers. But in the recursion case, you keep all of them plus the exception frame until the recursion rewinds. That's not memory efficient in Python or C.