How do I get data from one function to another - python-3.x

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

Related

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!")

It asks to give the values but instead of giving an answer. It is giving me None

Here I created a module.
class Employee:
def __init__(self):
self.name = input("Enter your name: ")
self.account_number = int(input("Enter your account number: "))
def withdraw(self): # it receives values from for
if withdraw1 > current_balance:
print ("You have entered a wrong number: ")
else:
print ("The current balance is: ", current_balance - withdraw1)
import TASK2 # I am importing the module I created
c = TASK2.Employee()
def for(self):
c.withdraw1 = int(input("enter number: "))
c.current_balance = int(input("Enter the current balance: "))
d = method(c.withdraw) # here I am trying to pass the values to withdraw
print (d)
The problem I get is that although it asks for the values instead of giving me an answer it gives me None.
Here's my take on your code.
# TASK2.py
class Employee:
def __init__(self):
self.name = input("Enter your name: ")
self.account_number = int(input("Enter your account number: "))
# make sure you initialise your member variables!
self.withdraw_val = 0 # withdraw1 is ambiguous, so I use withdraw_val instead
self.current_balance = 0
# receives values from for ### no it doesn't, right now, it GIVES values TO your "for" function
def withdraw(self):
if self.withdraw_val > self.current_balance: # remember to use "self." to
# access members within the class
print ("You have entered a wrong number: ")
else:
# again, remember "self."
print ("The current balance is: ", self.current_balance - self.withdraw_val)
# TASK2sub.py
import TASK2
c = TASK2.Employee()
def for_employee(employee): # (1) don't use "self" outside a class
# it's contextually unconventional
# (2) "for" is a keyword in Python, don't use it for naming
# variables/functions, it'll mess things up
employee.withdraw_val = int(input("Enter value to withdraw: "))
employee.current_balance = int(input("Enter the current balance: "))
return employee.withdraw_val # not entirely sure what you want to return
# but you should definitely return something
# if you're going to assign it to some variable
d = for_employee(c.withdraw()) # "for_employee" function needs a return statement
# ".withdraw()" method should also require a return statement
print(d)
Note: I'll be referring to your original for function as for_employee from now on. Also note that I'm still hazy about what you're trying to accomplish and that there is most probably a more suitable name for it.
Since your original for_employee function didn't return anything, it returns None by default. (This explains the output you saw.)
I think you're misunderstanding how functions work in general. For example,
d = for_employee(c.withdraw())
print(d)
Your comment for the .withdraw() method is inaccurate.
"it receives values from for"
More accurately, c.withdraw() will first be computed, then whatever it returns is passed into the for_employee function as a parameter. Instead of "receiving values from", the withdraw method "gives values to" the for_employee function.
Something more reasonable would be
c.withdraw() # on a line by itself, since it doesn't return anything
d = for_employee(c) # pass the entire object, since you'll be using self.withdraw_val and whatnot
print(d)
Another issue is with conventional naming. This is what I get from the IDLE (with Python 3.7) when defining a function named for
>>> def for(a): return a
SyntaxError: invalid syntax
Again, for is a keyword in Python, don't use it for naming your variables, functions, or classes.
With self, it's less severe (but I could see that it's confusing you). self is more of a convention used in class methods. But for_employee isn't a class method. So conventionally speaking, the parameter shouldn't be named self.
(I find the code spaghetti-ish, it might benefit if you refactor the code by moving the for_employee method into the class itself. Then it would completely make sense to use self.)

Defining function difficulties ["NameError: name 'number' is not defined"]

Okay, trying to make a simple game of Guessing Numbers but I can't find the mistake in this code. Still pretty new to python so probably the reason why but I can't figure out what is wrong with it.
import random
from time import sleep
def start():
print("Welcome To The Guessing Game \n Try to guess the number I'm thinking of \n Good luck!")
selectRandomNumber()
guessCheck(number, numberInput=1)
def restart():
print("Creating new number ...")
sleep(1)
print("OK")
selectRandomNumber()
guessCheck(number,numberInput=1)
def selectRandomNumber():
number = random.randint(0,1000)
tries = 0
return
def tryAgain():
while True:
try:
again = int(input("Do you want to play again? y/n:"))
except ValueError:
print("Couldn't understand what you tried to say")
continue
if again == "y" or "yes":
print("Awesome! Lets go")
restart()
elif again == 'n' or "no":
print("Goodbye!")
break
else:
print("Not a valid option")
continue
def guessCheck(number,numberInput=1):
while True:
try:
numberInput = int(input("What number do you think it is?: "))
except ValueError:
print("Couldn't understand that. Try again")
continue
if numberInput > number:
print("Too high")
tries += 1
continue
elif numberInput < number:
print("Too low")
tries += 1
continue
elif numberInput == number:
print("Congrats! You got my number")
tryAgain()
number = selectRandomNumber()
print(number)
start()
Every time I try to run the program I keep getting the same mistake.
It tells me:
Traceback (most recent call last):
File "python", line 60, in <module>
start()
File "python", line 8, in start
guessCheck(number, numberInput)
NameError: name 'number' is not defined
Don't quite understand what that means.
Some help would be appreciated. Thanks!
* UPDATE *
Was able to fix the part about defining the variable but now new problem happened where when I try to run
Same code as before but added
guessCheck(number,numberInput=1)
and also added the variable number at the end
number = selectRandomNumber()
print(number)
start()
when I run it I get this
None # this is from `print(number)` so instead of getting a number here I'm getting `None`
Welcome To The Guessing Game
Try to guess the number I'm thinking of
Good luck!
What number do you think it is?:
The Traceback is telling you this:
We got to start().
start() called guessCheck().
We tried to pass two pieces of information to guessCheck(): the variable names number and numberInput.
We don't have those variables defined yet! numberInput doesn't get defined until once we've already started guessCheck(), and number isn't actually defined anywhere.
As Manoj pointed out in the comments, you probably want number to hold the output of selectRandomNumber(). So, instead of just calling selectRandomNumber() in start(), try number = selectRandomNumber() instead.
You can add a print(number) on the line right after that to make sure number has a value assigned to it.
Now number has a value, going into your call to guessCheck(). That still leaves numberInput undefined though. You can set a default value for function arguments like this:
guessCheck(number, numberInput=1)
That way, when guessCheck is called but numberInput hasn't been defined yet, it will automatically give it the value 1 until you set it explicitly.
You may encounter other issues with your code the way it is. My advice would be to start really simply - build up your game from each individual piece, and only put the pieces together when you're sure you have each one working. That may seem slower, but trying to go too fast will cause misunderstandings like this one.

local variable referenced before assignment but variable is global [duplicate]

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.

Tkinter button command argument

I have been looking into tkinter with python as I am seriously interested in GUi's and thought it would be a great place to start. I went through a good few tutorials like The New Boston set and one or two theres to grab hold of the basics. Now I am trying to pass an 'argument' through a botton so that my program will move on to my IF statement and I am having no joy.
Please find attached code:
try:
from tkinter import *
except ImportError:
from Tkinter import *
eod = 'no'
selection = []
selection1 = 'nothing'
while eod != 'yes':
def beer():
selection.append('Beer')
selection1 = 'Beer'
def wine():
selection.append('Wine')
def whiskey():
selection.append('Whiskey')
welcomeGUI = Tk()
welcomeGUI.geometry('400x200+100+200')
welcomeGUI.title('Drinks Despenser')
welcomLabel1 = Label(welcomeGUI, text='Drinks-O-Matic', font='Times 22 bold').grid(row=0,column=2)
welcomLabel2 = Label(welcomeGUI, text='Please select drink', font='Times 16 bold').grid(row=1,column=2)
beerButton = Button(welcomeGUI, text='Beer', font='Times 16 bold',command=beer()).grid(row=6,column=1)
wineButton = Button(welcomeGUI, text='Wine', font='Times 16 bold').grid(row=6,column=2)
whiskeyButton = Button(welcomeGUI, text='Whiskey', font='Times 16 bold').grid(row=6,column=3)
if selection1 is 'Beer':
welcomeGUI.destroy()
beerGUI = Tk()
beerGUI.geometry('400x200+100+200')
beerGUI.title('Beer Despenser')
beerGUI.mainloop()
welcomeGUI.mainloop()
Ok there is a lot going on here so I have a couple of things that I think will help you.
You need to move your def out of the while loop for all the functions. They should be defined only once in the beginning of the file.
Also, you are assigning variables to the Button object after you call the grid method. That method returns None so you shouldn't do that because you are assigning variables None instead of the actual button object as you intend to. You should assign the variables the button object alone and then call varname.grid() later.
Finally, to address your question: when you write command=beer() you are once again calling the function beer and assigning its return value to the command parameter. When you are using Tkinter you must assign only the function name to the command parameter such as command=beer. However, if you have to pass it arguments you can use lambda. For example: command=lambda: beer(arg1, arg2).
P.S.
When comparing strings you should say
if selection1 == "Beer":
not
if selection1 is "Beer":
is tests for identity not equality and you want to test equality.
EDIT: You also should unindent the try at the top of your file.
Also because selection1 is a local variable in the function beer it won't work, you need to declare it a global
def beer():
global selection1
selection.append('Beer')
selection1 = 'Beer'
Furthermore, you need to destroy the window or the if statement in the while loop won't run.
def beer(window):
global selection1
selection.append('Beer')
selection1 = 'Beer'
window.destroy()
and then you need to pass the welcomeGUI Tk instance to the function like so
beerButton = Button(welcomeGUI, text='Beer', font='Times 16 bold',command=lambda: beer(welcomeGUI)).grid(row=6,column=1)
One last thing. I would remove the while loop all together and have a button on the beer window to call back the main welcome window because putting two mainloops in the while loop is not going to be a good thing.

Resources