0

I have a piece of code to ask a user for a number within a certain range. I know what works, in part from looking at questions like this: Limiting user input to a range in Python

In playing with it, though, I made the function below, which works in all cases except when there's text entered at any point prior to a correct entry. For example, inputting 3, 2, f, 3, 2, 5 produces the following:

TypeError: '<=' not supported between instances of 'int' and 'str'

In all other cases the code works.

Code:

def pick_a_number():
    user_number = input("Pick a number from 5 to 10, inclusive. ")

    try:
        user_number = int(user_number)
    except:
        print("Not a valid entry. Try again. ")
        pick_a_number()

    if 5 <= user_number <= 10:
        print("You picked " + str(user_number))
    else:
        print("Number out of range. Try again.")
        pick_a_number()

pick_a_number()

Why does the code fail the way that it does? I think it's something to do with the repeated calls to the function within the function, but I'd like to understand what's happening.

martineau
  • 119,623
  • 25
  • 170
  • 301
Stidgeon
  • 2,673
  • 8
  • 20
  • 28
  • 4
    Calling `pick_a_number` recursively does not alter the value of the variable `user_number`. You would be better off with a `while loop`. Recursion is not an appropriate way to approach this situation in Python. Also your function does not return anything. – khelwood Dec 31 '19 at 22:38
  • Lines after the `except` run if the input is not an int, after the `pick_a_number()` call finishes – alamoot Dec 31 '19 at 22:39
  • See https://stackoverflow.com/questions/23294658/asking-the-user-for-input-until-they-give-a-valid-response – Thierry Lathuille Dec 31 '19 at 22:39
  • 3
    The answer to "Why does my code do X?" is almost always: Add `print()` statements everywhere until it becomes clear. – JacobIRR Dec 31 '19 at 22:41
  • It has a section specifically about why to avoid the recursive approach you are attempting. – tripleee Dec 31 '19 at 22:56
  • You could also add a `return` to your `except` block, prohibiting further execution after calling the function again recursively. – Jan Christoph Terasa Jan 01 '20 at 00:51

1 Answers1

2

If you trap the exception you can print your vars to see what's wrong:

>>> def pick_a_number():
...     user_number = input("Pick a number from 5 to 10, inclusive. ")
...     try:
...         user_number = int(user_number)
...     except:
...         print("Not a valid entry. Try again. ")
...         pick_a_number()
...     try:
...         if 5 <= user_number <= 10:
...             print("You picked " + str(user_number))
...         else:
...             print("Number out of range. Try again.")
...             pick_a_number()
...     except:
...         print("@@ user_number during failure is : {}".format(user_number))
...         print("@@ type(user_number) during failure is : {}".format(type(user_number)))
...
>>> pick_a_number()
Pick a number from 5 to 10, inclusive. 3
Number out of range. Try again.
Pick a number from 5 to 10, inclusive. 2
Number out of range. Try again.
Pick a number from 5 to 10, inclusive. f
Not a valid entry. Try again.
Pick a number from 5 to 10, inclusive. 3
Number out of range. Try again.
Pick a number from 5 to 10, inclusive. 2
Number out of range. Try again.
Pick a number from 5 to 10, inclusive. 5
You picked 5
@@ user_number during failure is : f
@@ type(user_number) during failure is : <class 'str'>
JacobIRR
  • 8,545
  • 8
  • 39
  • 68
  • I see what you're getting at here, but what I don't understand is why the error doesn't happen until later. When 'f' is run through the function, it hits the exception and calls the function again, which then completes a few more rounds. It's only after the 'successful' entry that the error gets thrown. – Stidgeon Dec 31 '19 at 22:52
  • 1
    Only in the success case does the recursive stack fully "unwind". Add a print statement in every block so you know what is happening. Recursion is best understood visually. I can delete my answer since I mostly just wanted to show you the debugging technique I mentioned in my comment. – JacobIRR Dec 31 '19 at 22:55
  • 2
    @Stidgeon, ...the thing to keep in mind is that when you call something, that's not a "goto", it's a *function call*; when the function returns, the code picks back up at the place things were at before the call was made. (The stack is a detail of how storage of local variables for different levels of the call tree is traditionally stored, along with parameters and return values). – Charles Duffy Dec 31 '19 at 23:10