0

I just started coding in Python a few weeks ago and I'm building small projects to build my knowledge. Today I started building a Hangman but I've been stuck here for 2H+.

My CODE:

   #Imports-----------------------------------------------
from time import sleep
from random import choice



#Lists------------------------------------------------
wordList = ["Mortgage", "Marsupial", "Bloodshed", "Adventure", "Chronological", "Issue"]

#Functions---------------------------------------------


def wordGuessing():
    NumberOfMisses = 0
    WordOrLetter = input("Do you want to guess the word or a letter? Type 'Word' or 'Letter?'")
    if WordOrLetter == "Word":   #------------------------------------------------------------------------------If guessing Word
        guessingWord = input("Try guessing the word! (First letter is Uppercase, the rest is lowercase)")
        if guessingWord == chosenWord: #--------------------------------------------------If correct
            print("Hurray! You got it!")
        else:    #--------------------------------------------------If wrong
            print(NumberOfMisses)
            if NumberOfMisses == 6:
                print("Oh no! Too many mistake! Game Over!")
                exit()
            else:
                print("Oops. You didn't guess correctly! ")
                print(NumberOfMisses)
                NumberOfMisses += 1
                wordGuessing()

    elif WordOrLetter == "Letter":  #--------------------------------------------------If guessing Letter
        guess = input("Guess a letter! (Type in lowercase)")
        print("You chose the letter " + guess + "!")
        sleep(1)
        if guess in chosenWord:
            print("You got it right!") 
        else:
            print("Oops! That letter isn't in the word!")
            print(NumberOfMisses)
            NumberOfMisses += 1
            print(NumberOfMisses)
            wordGuessing()



#CODE--------------------------------------------------
print("Hi! Welcome to the Hangman Python Edition")
chosenWord = choice(wordList)
chosenWordLength = len(chosenWord)
print(chosenWord) #Remove later----------------------------------------REMOVE LATER
print("I have chosen a " + str(chosenWordLength) + " letter word!")
wordGuessing()

My particular problem comes with the NumberOfMisses variable. In the given code, it works well but since I restart the function to keep playing, it resets to 0.

If I move the variable outside of the function, whether next to imports or in CODE, it gives me the error in the Title. I've tried global, I've checked the control flow, I've tried everything I can and truly cannot comprehend the problem. The print(NumberOfMisses) is intentional, to see if there's an update.

Can anyone help me out?

j-i-l
  • 10,281
  • 3
  • 53
  • 70
  • 1
    well, not read much of your question, but consider that the standard way to name variables in Python is using underscore, so for example `NumberOfMisses` should be `number_of_misses` and so on. Same for functions. Search for "pep8". – Paolo Oct 02 '21 at 16:59
  • Does [this](https://stackoverflow.com/questions/17928791/python-how-to-reference-a-global-variable-from-a-function) help? – Robin Gertenbach Oct 02 '21 at 17:03
  • 4
    Note: While @Paolo is right about the standard way of variable names, it is perfectly valid to call your variable `NumberOfMisses`, the name is not the issue here. – j-i-l Oct 02 '21 at 17:09

2 Answers2

1

You set NumberOfMisses=0 at the beginning in the function body. This means that within the call of wordGuessing the variable NumberOfMisses will refer to the value 0 in the beginning. This no matter how often you call wordGuessing and no matter if the call happens within the body of the very same function.

The smallest adaptation to the existing code that will lead to the behavior you want is to use a global variable.

To do so, replace the first line in the body of wordGuessing:

NumberOfMisses = 0  # remove this from function body

with

global NumberOfMisses  # add this at the beginning of function body

and add

NumberOfMisses = 0

at the very end of your script, just above the call of wordGuessing:

print("I have chosen a " + str(chosenWordLength) + " letter word!")
NumberOfMisses = 0  # this is new
wordGuessing()

I hope that helps to get you going!


Using global variables can be quite useful, but it is also not a practice that you should rely too much on. As @juanpa.arrivillaga mentions in a comment it is considered bad practice. Have a look at this SO answer if you want to learn more about it. If you want to familiarize a bit more with the global statement, good place to start is always the w3schools, here would be an intro to the global statement.

j-i-l
  • 10,281
  • 3
  • 53
  • 70
  • Using global variables like this is considered a bad practice – juanpa.arrivillaga Oct 02 '21 at 17:58
  • @juanpa.arrivillaga I'm not saying that this is the best way to do. It is the way that takes the least adaptation of the existing code. There are other thing in the code I'd change much before addressing bad practices. But learning is a gradual process, one thing at a time, this is how you move forward, so I'd prefer to not overload an answer. But this is just me, feel free to contribute an answer which better follows guides and practices. I'd sure vote for it! – j-i-l Oct 02 '21 at 18:05
1

So @jojo's answer basically gives the solution to the problem that you are facing. I just want to add why the error you are facing is actually occuring (As you said, "I've tried everything I can and truly cannot comprehend the problem.").

Check out this example:

x = 10
def bar():
    print(x)
bar()
10

works, but this code:

x = 10
def foo():
    print(x)
    x += 1

results in an UnboundLocalError:

foo()
Traceback (most recent call last):
  ...
UnboundLocalError: local variable 'x' referenced before assignment

Quoting the Official Python FAQ,

This is because when you make an assignment to a variable in a scope, that variable becomes local to that scope and shadows any similarly named variable in the outer scope. Since the last statement in foo assigns a new value to x, the compiler recognizes it as a local variable. Consequently when the earlier print(x) attempts to print the uninitialized local variable and an error results.

So, although, it is very common to assume that python interprets line by line and if there is something written in the end, it will only run when the interpreter reaches that line, in the case of functions, classes and other derivatives, python skims through the definition to declare and associate scope of variables before execution.

Again, quoting the FAQ,

In Python, variables that are only referenced inside a function are implicitly global. If a variable is assigned a value anywhere within the function’s body, it’s assumed to be a local unless explicitly declared as global.

Hence, the solution to use global declaration will solve your issue.

Daniel Walker
  • 6,380
  • 5
  • 22
  • 45
Manu S Pillai
  • 899
  • 8
  • 13
  • Great addition explaining shadowing and scope of a variable +1 – j-i-l Oct 02 '21 at 18:16
  • So during execute when python meets a function definition it isn't actually skipping the rest of code but it checks the scope of variables (every time it meets a variable)? – ado sar Jul 01 '22 at 22:15