0

I know that try/except blocks do not have separate scopes, and I also know the actual solution to this problem, though I want to know why it happens. I am making a simple guessing game for my sdev class and while I can just add guess_num before the try/except block to make it work, why do I have too?

I have it loop back to main, but if I go through the program and do not enter a number as input, it eventually brings me back to the input again, but if I then do a number, it will give me this error:

Traceback (most recent call last):
  File "C:\Users\Jexxy\Documents\MyRepo\Guessing_Game.py", line 35, in <module>
    main()
  File "C:\Users\Jexxy\Documents\MyRepo\Guessing_Game.py", line 31, in main
    if guess_num > 0 and guess_num < 100 and isinstance(guess_num, int):
UnboundLocalError: local variable 'guess_num' referenced before assignment
def main():
    random_num = random.randrange(1, 5)
    try:
        guess_num = int(input('''Welcome to the guessing game!
                 A Random number between 1-100 has already been generated!
                 When ready, please enter a number between 1-100 to make your guess: '''))
    except ValueError:
        try_again = input("You must enter a number! Try again? Y/N: ")
        if try_again.lower() == "y":
            main()
        else:
            exit(0)

    if guess_num > 0 and guess_num < 100 and isinstance(guess_num, int):
        print(guess_num)
Jexxy
  • 3
  • 1
  • Because if the variable isn’t defined in the `try` block because of an exception, then it isn’t defined in the `except` block either, but the `if` after it will be executed either way. – deceze Aug 21 '21 at 17:34

4 Answers4

1

When you loop back to main because you deliberately enter an invalid input, eventually main() exits and you are back just after the call. It is at this point the function drops down to the if guess_num ... line and guess_num is not defined.

Firstly you shouldn't be using recursion for your solution, but secondly to just avoid the error, add a return just after main().

quamrana
  • 37,849
  • 12
  • 53
  • 71
1

there isn't a specific scope to try and except like a function or a class. but when you finish the try you don't go to the except you go to the next code that isn't in that except. that is why you also get the error because if you don't go to the except you don't have a variable called try_again assigned in the memory.

yotam rec
  • 587
  • 4
  • 15
1

If you get a ValueError, you call main again and that instance does the calculation. But when that instance returns, you are back to the original main that didn't assign anything to guess_num. This original instance tries the compare, and gets the exception. Each call to main creates its own scope for local variables. Its not the try/except scope, its the called function scope.

The solution is to return in the exception case. You've already printed guess_num in one of the recursive calls, so there is nothing else to do.

def main():
    random_num = random.randrange(1, 5)
    try:
        guess_num = int(input('''Welcome to the guessing game!
                 A Random number between 1-100 has already been generated!
                 When ready, please enter a number between 1-100 to make your guess: '''))
    except ValueError:
        try_again = input("You must enter a number! Try again? Y/N: ")
        if try_again.lower() == "y":
            main()
            return
        else:
            exit(0)

    if guess_num > 0 and guess_num < 100 and isinstance(guess_num, int):
        print(guess_num)
tdelaney
  • 73,364
  • 6
  • 83
  • 116
1

guess_num is only set if input() or int() succeeded. If either of those throws an exception, Python doesn't have a value to assign to guess_num.

Your real mistake is to use recursion however. When you handle the exception, you call main() again. Eventually, main() returns and the code continues from the point where you called main(). Each recursive call to main() has their own set of local variables, so by the time main() returns, all successful runs of main() are gone, taking their local variables with them. Even if guess_num was set in one of the recursive calls, it doesn't survive to live in a parent function run.

So what really happens is this:

  1. you enter a non-integer value, and an exception is raised
  2. you call main(), and run your code successfully this time.
  3. main() returns, and now you try to access the local variable guess_num, but it was never set.

Don't use recursion. Use a loop. Or, if you must use recursion, Use return main(), so the function exits once the recursive call to main() ends.

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