local variable referenced before assignment but variable is global [duplicate] - python-3.x

This question already has answers here:
Variables declared outside function
(3 answers)
Closed 2 years ago.
I have some code and I have assigned a variable as global but when trying to use the variable as validation in a separate function, it throws an exception.
This is the unfinished code (I know it won't work properly at the moment but I want to fix this first) for some school homework, I know there are probably much more efficient was to achieve my purpose but I want to understand why this isn't working.
def mainFunc():
nameList = []
print("1. Add name \n2. Display list \n3. Quit\n")
choice = displayMenu()
if choice == 1:
addName()
elif choice == 2:
displayList()
else:
print("Program Terminating")
def displayMenu():
global answer
answer = int(input("Please enter your choice: "))
answerCheck()
def answerCheck():
if answer == 1 or answer == 2 or answer == 3:
return(answer)
else:
answer = input("Invalid selection, please re-enter: ")
answerCheck()
def addName():
position = int(input("Please enter the position of the name: "))
name = input("Please enter the name you wish to add: ")
remove = nameList[position-1]
nameList.remove(remove)
nameList.add(name,position)
print(nameList)
mainFunc()

Python treats your variable answer as a local variable as in your answerCheck() function, under the else clause, there is an assignment done to the variable answer. Since there is an assignment involved within the local scope, python treats the variable to be in the local scope and this gives your issue. As long as you don't use assignment within your function, the global variable will be read.
You can test this by commenting out the line answer = input("Invalid selection, please re-enter: ") and calling the function. It should work fine.
In order to get your code to work, let python know that you are referencing the global variable using global answer in your answerCheck() function.

Related

NameError: free variable 'addcontact' referenced before assignment in enclosing scope

please be kind with my n00b question but it is driving me crazy and googling it didn't help much unfortunately.
I am trying to write a simple phonebook script using an empty dictionary as a learning exercise but as I am self taught there is no one else to turn to.
So here's what's happening,
I want to write a menu function that includes subfunctions for adding, deleting and editing {name:number} contacts
it is mostly working except for when I try to call the add function from within the edit
this is part of the menu code including the add routine
def menu():
selection = (int(input("""1:View phonebook
2:Add contact
3:Delete contact
4:Search for a contact and if necessary edit it
5:Save and Exit
What would you like to to?""")))
if selection == 1:
if len(phonebook)== 0:
print("Phonebook is empty, please add a contact first.")
loop = input("Press Enter to continue ...")
menu()
else:
print(phonebook)
loop = input("Press Enter to continue ...")
menu()
elif selection == 2:
def addcontact():
first = (str(input("First name: ")))
last = (str(input("Last name: ")))
num = (str(input("Number? ")))
phonebook.update({last + " " + first: num})
loop = input("Contact saved, press Enter to continue.")
menu()
(other stuff...)
menu()
and this is the update subroutine that's giving me trouble
elif selection == 4:
def search_and_edit():
search = (str(input("Please type the exact name of the contact you are looking for: ")))
if search in phonebook.keys():
print("Name: ", search, "Number: ", phonebook[search])
edit = (str(input("Do you wish to edit this contact? Y/N")))
if edit == "N" or edit == "n":
menu()
if edit == "Y" or edit == "y":
phonebook.pop(search)
addcontact()
else:
print("Contact not found.")
loop = input("Press Enter to continue.")
menu()
and this is the error message from PyCharm
File "C:\Users\Derek\PycharmProjects\pythonProject\phonebook\phonebook.py", line 78, in search_and_edit
addcontact()
NameError: free variable 'addcontact' referenced before assignment in enclosing scope
Process finished with exit code 1
what am I doing wrong here? the other callbacks work fine ("loop" callbacks to return to the main menu for example) but the addcontact() one fails with a variable error message even though it's a function
welcome aboard. The issue boils down to the addcontact being defined within the scope of a conditional (if...elif...else) and as a result not visible to search_and_edit which is defined in a different and mutually exclusive branch. So, when you chose 4, the program hadn't really entered 2 and the addcontact had not been "created" yet.
If you wish to make a function available at multiple places, then define it where it would in scope for all the callers.

How do I get data from one function to another

I read about "random" and it got me thinking about helping my kid in reading and writing by making a program where a word is shown and she needs to type it. Because it's a small program I could do it quite easliy with procedural programming, but to make it more 'attractive' to her I decided to fool around with tkinter.
Tkinter forces to create functions which can be called through 'command' and now I have a problem.. If I run the check() function, it doesn't get the variables from the dictee() function. I found several answer from nesting a function in a function (undefined variable problems), or passing arguments with return (ended up in recursion), using global (the list of words wouldn't empty) etc etc.. I couldn't get any of them working... I've been looking for answers, but I can't find the correct solution.. Anyone care to shed their light?
Thanks!
"""import nescessary."""
import sys
import random
def main():
"""Setting up the game"""
print("Aliyahs reading game.'\n")
begin = input("do you want to start? yes or no.\n\n")
if begin == "yes":
dictee()
def dictee():
"""Show random words and ask for input."""
words_correct = 0
words_wrong = 0
vocabulary = ['kip', 'hok', 'bal', 'muis', 'gat'
]
words_passed = []
while True:
if vocabulary == []:
print("\n\nThose were all the words.")
print("Words correct: %d" % words_correct)
print("words wrong: %d" % words_wrong)
one_more_time = input("Do you want to go again? yes or no")
if one_more_time == "no":
print("See you next time.")
input("\nPush enter to close.")
sys.exit()
else:
main()
word = random.choice(vocabulary)
print('\x1b[2J')
print("{}".format(word))
print("\n\n")
words_passed.append("{}".format(word))
vocabulary.remove("{}".format(word))
answer = input("Write the word you saw:\n\n")
check()
def check():
'''Cross check word shown with answer given'''
if answer == word:
print("Nice")
words_correct += 1
else:
print("2bad")
words_wrong += 1
try_again = input("\n\nContinue? yes or no\n ")
if try_again.lower() == "no":
exit_game()
else:
dictee()
def exit_game():
'''summarize results and exit after pushing enter'''
print("Words correct: %d" % words_correct)
print("Words wrong: %d" % words_wrong)
input("\nPress Enter to exit.")
sys.exit()
if __name__ == "__main__":
main()
Ive just made few changes here, and run it and got no errors because I dont know what to type in order to get the right results. Anyways all the changes are related to global and redefinition of values.
# say these outside all the functions, in the main block
words_correct = 0
words_wrong = 0
vocabulary = ['kip', 'hok', 'bal', 'muis', 'gat']
words_passed = []
def dictee():
global word, answer
..... #same bunch of codes
def check():
global answer, words_correct, words_wrong
.... #same bunch of codes
Why do we have to say global? Basically because when we define variables they either get defined on local scope or global scope, variables defined on main block(outside of all function) are on global scope, while inside functions are on local scope. Variables defined on global scope can be accessed anywhere and that on local can only be accessed from within where its defined.
Where do we have to use global? We say global where we define the variable or we redefine, or at least this is what I know of. We need to say global where we declare the variable, like, a = 'good', we also need to say global if we are changing it, a = 'bad' or a += 'day' because we are assigning a new value to it. Using global outside of all functions, on the main block, is useless.
Why are we declaring words_correct and words_wrong outside all functions? It is simply because if you declare and set its value to 0 inside dictee(), each time the function is run the value of those variables will change to 0, which means score will always be 0 or 1, so we define it once only, in the main block. Same logic applies to the two lists(vocabulary and words_passed), each time function runs they reset the list to the full list of words, so to get rid of that, just define it once in the main block.
I also think using parameters here might need re-structuring of your code as your calling both function from each other.
Note that we only say global on functions, out side functions all defined variables are open to global scope and can be accessed from anywhere in the code.
PS: This is just my understanding of global over the span of 4 months, do correct me if im wrong anywhere, thanks :D

Can you create an input that changes?

I am creating a quiz-like program and decided to define the 'while True' loop function (shown below) as a function. To do that, I need the input to change. However, I can't seem to do that no matter how many times I re-format the question. Here is the code:
def whiletrueloop():
while True:
choicenumber=input(choice)
if choicenumber.lower() not in (options):
print(repeatsentence)
else:
break
choice=("What is bigger, an elephant or a mouse?")
choicenumber=("choice1")
options=('elephant','mouse')
repeatsentence=("That isn't an answer!")
whiletrueloop()
if choicenumber=="elephant":
print("Correct!")
elif choicenumber=="mouse":
print("Wrong!")
I think the main problem with the code has to do with the scope of your choicenumber variable. You set it as a global variable with the line
choicenumber = ("choice1")
In the function, it is assigned locally to the input value, but it is the global scope variable that is used in the if-else decision at the end of the code. You can test this by inserting this option at the end of your code:
elif choicenumber == "choice1":
print("Global!")
You'll always get "Global!" displayed, because the local value from the function is ignored in place of the global value.
There are a few ways around this. One is to declare the choicenumber variable as global inside the function:
while True:
global choicenumber
choicenumber=input(choice)
However, using global variables is considered bad practice, for reasons that you can look up.
An alternative is to use the function to return the value of choicenumber in response to the function call:
def whiletrueloop():
while True:
choicenumber=input(choice)
if choicenumber.lower() not in (options):
print(repeatsentence)
else:
return choicenumber
choice=("What is bigger, an elephant or a mouse?")
options=('elephant','mouse')
repeatsentence=("That isn't an answer!")
choicenumber = whiletrueloop()
if choicenumber=="elephant":
print("Correct!")
elif choicenumber=="mouse":
print("Wrong!")

Python 3 How to Ask user for specific input and reject invalid inputs

I have a question on how to check a user's input and make sure they are returning a specific string. Currently, the function when called will ask the user for their input. However, if they choose a string that is not part of the function, the else statement will execute and continue the code. I am trying to figure out how to loop this function, until a user inputs one of the strings that the function is looking for. Can anyone help me with this? I am new to python and would appreciate any help.
def dwarf_class_definer(dwarf_class):
if dwarf_class == "Thane":
print("'Ahhh Nobility'")
elif dwarf_class == "Mekanik":
print("'Interesting a Mechanic'")
elif dwarf_class == "Ancestrite":
print("'A spiritualist. I see...'")
elif dwarf_class == "Prisoner":
print("'Never met a gen-u-ine 'Last chancer.'")
elif dwarf_class == "Civilian":
print("'ehhh a civilian? Wut you doing here?'")
else:
print("You aren't choosing a valid class.")
dwarf_class = input("Which Class will you choose?: ")
dwarf_class_definer(dwarf_class)
A while loop will keep going until you tell it not to anymore. You can see when an expected value is supplied, the break command will terminate the while loop. A dictionary can also make your code a lot cleaner and easier to maintain compared to a bunch of if statements.
dwarf_classes = {
"Thane": "'Ahhh Nobility'",
"Mekanik": "'Interesting a Mechanic'",
"Ancestrite": "'A spiritualist. I see...'",
"Prisoner": "'Never met a gen-u-ine 'Last chancer.'",
"Civilian": "'ehhh a civilian? Wut you doing here?'",
}
while True:
dwarf_class = input("Which Class will you choose?: ")
if dwarf_class in dwarf_classes.keys():
print(dwarf_classes[dwarf_class])
break
print("You aren't choosing a valid class.")
example:
$ python3 so.py
Which Class will you choose?: Python!
You aren't choosing a valid class.
Which Class will you choose?: Prisoner
'Never met a gen-u-ine 'Last chancer.'

How to edit strings in a list

What I want is this - I have a list of names created from user input. now i have to come up with a way for the user to edit a name by entering the name that they what to edit and then obviously edit it into what they want and store it in the list.
if it helps heres everything I have so far. def edit() is where im struggling.
def mainMenu():
print("\nMAIN MENU")
print("1. Display Members:")
print("2. Add A Member(s):")
print("3. Remove A Member:")
print("4. Edit Member:")
print("5. Exit:")
selection = int(input("\nEnter Choice: "))
if selection == 1:
display()
elif selection == 2:
add()
elif selection == 3:
remove()
elif selection == 4:
edit()
elif selection == 5:
exit()
else:
print("Invalid choice, enter 1-5.")
mainMenu()
def display():
#displaying roster...
print(roster)
mainMenu()
def add():
#adding team members...
size = int(input("How many players are you adding?"))
global roster
roster = [0] * size
for i in range(size):
roster[i] = input("Enter members name: ")
roster.append(roster)
mainMenu()
def remove():
#removing a team member...
roster.remove(input("Enter member to be removed: "))
mainMenu()
def edit():
#edit a team member...
roster.insert(input("Enter Name to be edited: "))
mainMenu()
mainMenu()
Removing and adding elements are pretty easy in python because they are directly supported by the language. Each of them can be translated into only one instruction.
When something doesn't seem very obvious, such as the editing functionality you are trying to implement, try breaking it down to things that can be expressed as a simple operation that holds one one line (even if not in order).
To find the answer I thought this: somewhere in my code, I want to type roster[ind_name_to_edit] = new_name.
I knew then that before typing this, I would want to find the value of ind_name_to_edit. This can be done by roster.index(name_to_edit). And you already know how to get the name to be edited and the name to edit ;)
If you're still unsure how to do what you want to do, re-read this answer and see the documentation of the index method of list in python3 and maybe some examples here.
N.B: If your list is supposed to be sorted in some way, you should implement your own search algorithm instead of using index, and you should consider re-sorting the list after the edit. I know it's a long shot but just in case.

Resources