I'm new to the stack overflow website, python, and programming as a whole. So pardon me if the title or body of this question isn't apt.
I'm trying to create a simple calculator program in python, which performs only four operations, namely, addition, subtraction (difference), multiplication and division.
Here's my code:
print("Welcome to the calculator \n")
num1 = int(input("Enter the first number \n"))
num2 = int(input("Enter the second number \n"))
operation = (input("""Enter the symbol of the operation to be performed. Your choices are:
+
- (Difference)
*
/
\n """))
add = num1+num2
sub1 = num1-num2
sub2 = num2 - num1
product = num1*num2
quotient = num1 / num2
if operation == "-" :
if num1 > num2:
print(sub1)
else:
print(sub2)
elif operation == "+" :
print(add)
elif operation == "*" :
print(product)
elif operation == "/" :
if num2 == 0:
print("Sorry, can't divide a number by zero")
else:
print(quotient)
else:
print("Please enter a valid operator from among the ones provided above")
Everything runs fine, except, when I enter zero as num2, and no matter what operator I select, the output
is this:
Traceback (most recent call last):
File "test4.py.txt", line 19, in <module>
quotient = num1 / num2
ZeroDivisionError: division by zero
Help would be greatly appreciated.Thanks!
Find the quotient inside the if condition, not before checking num==0, otherwise the division by zero is already done, causing the error.:
elif operation == "/" :
if num2 == 0:
print("Sorry, can't divide a number by zero")
else:
quotient = num1 / num2
print(quotient)
quotient = num1 / num2
That's the line that is giving you the error. You should be checking if num2 is zero before calculating num1 / num2. You can do it as follows and your program would work fine.
quotient
if num2 == 0:
quotient = None
else :
quotient = num1 / num2
Else you could just declare quotient and calculate the quotient when user inputs the operator. If user enters /, then you can check if num2==0 and if it is you can give error message.
Working code -
print("Welcome to the calculator \n")
num1 = int(input("Enter the first number \n"))
num2 = int(input("Enter the second number \n"))
operation = (input("""Enter the symbol of the operation to be performed. Your choices are:
+
- (Difference)
*
/
\n """))
add = num1+num2
sub1 = num1-num2
sub2 = num2 - num1
product = num1*num2
quotient = None # Don't divide by num2 here or you will get error of divisionbyzero
if operation == "-" :
if num1 > num2:
print(sub1)
else:
print(sub2)
elif operation == "+" :
print(add)
elif operation == "*" :
print(product)
elif operation == "/" :
if num2 == 0:
print("Sorry, can't divide a number by zero")
else:
quotient = num1 / num2
print(quotient)
else:
print("Please enter a valid operator from among the ones provided above")
Since you are new, I will explain this in more detail so you actually know what you are doing wrong
Pay attention to these lines
add = num1+num2
sub1 = num1-num2
sub2 = num2 - num1
product = num1*num2
quotient = num1 / num2
When you do this it is actually performing the calculation and assigning it to the respective value (i.e. quotient = num1 / num2 will actually do the calculation and store the result in the quotient variable). So, you are doing every calculation every time regardless of your choice of operator. It would be better to assign the values only if that operator has been chosen like below.
print("Welcome to the calculator \n")
num1 = int(input("Enter the first number \n"))
num2 = int(input("Enter the second number \n"))
operation = (input("""Enter the symbol of the operation to be performed. Your choices are:
+
- (Difference)
*
/
\n """))
if operation == "-" :
sub1 = num1-num2
sub2 = num2 - num1
if num1 > num2:
print(sub1)
else:
print(sub2)
elif operation == "+" :
add = num1+num2
print(add)
elif operation == "*" :
product = num1*num2
print(product)
elif operation == "/" :
if num2 == 0:
print("Sorry, can't divide a number by zero")
else:
quotient = num1 / num2
print(quotient)
else:
print("Please enter a valid operator from among the ones provided above")
This part is just a suggestion. You could also make your Difference operation easier by using the following.
if operation == "-" :
sub1 = num1-num2
print(abs(sub1))
abs gets the absolute value, basically removes the negative.
You should calculate the results only after checking the operators (thus avoiding any errors caused by trying to divide by zero), this way you will also avoid unnecessary calculations:
elif operation == "/" :
if num2 == 0:
print("Sorry, can't divide a number by zero")
else:
quotient = num1 / num2
print(quotient)
New python amateur, and first time posting a question so excuse me if the structure of the question is not standard
Full code, if you understand what i am trying to achieve skip the intro, and skip the first four functions anyway (provided them if you want to execute the full code on your side):
import os, random
modes = ['Addition', 'Subtraction', 'Multiplication', 'Division']
global very_beginning, c_add, c_sub, c_mul, c_div, add_rounds, sub_rounds, mul_rounds, div_rounds, Round, correct, total_time
def mode_addition():
num1, num2 = random.randrange(1, 10), random.randrange(1, 10)
print(num1, " + ", num2)
return num1 + num2
def mode_subtraction():
num1, num2 = random.randrange(1, 10), random.randrange(1, 10)
while num1 <= num2:
num1, num2 = random.randrange(1, 10), random.randrange(1, 10)
print(num1, " - ", num2)
return num1 - num2
def mode_multiplication():
num1, num2 = random.randrange(1, 10), random.randrange(1, 10)
print(num1, " X ", num2)
return num1 * num2
def mode_division():
num2, num3 = random.randrange(1, 10), random.randrange(1, 10)
num1 = num2 * num3
print(num1, " ÷ ", num2)
return num3
def mode_var_names(mode):
return "mode_" + mode.lower() + "()", "c_" + mode.lower()[0:3], mode.lower()[0:3] + "_rounds"
def mental_math():
print("Mental math game: ")
global Round, correct, total_time, c_add, c_sub, c_mul, c_div, add_rounds, sub_rounds, mul_rounds, div_rounds
Round, total_time = 1, 0
correct, c_add, c_sub, c_mul, c_div, add_rounds, sub_rounds, mul_rounds, div_rounds = int(), int(), int(), int(), int(), int(), int(), int(), int()
while Round <= 20:
print('Round ' + str(Round), end=":\n\n")
roundmode = random.choice(modes)
mode_info = mode_var_names(roundmode)
print("This round will be about:", roundmode)
correct_answer = eval(mode_info[0])
exec(mode_info[2] + " += 1")
print("The answer is: ", end='')
roundanswer = int(input())
if roundanswer == correct_answer:
print("The answer {} is correct.".format(roundanswer))
correct += 1
exec(mode_info[1] + " += 1")
else:
print("The answer {} is incorrect. The answer should be: {}".format(roundanswer, correct_answer))
print("You got {} out of {} questions correct".format(correct, Round))
user = input("Press enter to continue, or type 'stop' to end this quiz\n")
if user == 'stop' or Round == 20:
result_page()
break
Round += 1
def result_page():
mark = round(correct / Round * 100)
os.system('cls')
print("Mental math game")
print('FIN\n\n')
print("You solved {} questions".format(Round))
print("You got {}% of the questions correct.".format(mark))
print("If you want detailed results type 'details'")
print("If you want to continue, type 'again', else just close this windows or press Enter")
user = input()
if user == "details":
for i in modes:
print("In", i, "You got ", eval(mode_var_names(i)[1]), "correct answers out of ",
eval(mode_var_names(i)[2]))
mental_math()
Intro:
I am putting together a mental math game as coding practice. It randomly chooses the mode from a list of strings: ['Addition', 'Subtraction', 'Multiplication' and 'Division'],uses its name to call the corresponding function which creates the question (for example if it chose Addition it would call a function called mode_addition)and keeps track of the number of correct answers of each mode out of the total of each mode as variables generated on the spot with a predetermined template (for example if it chose addition it would create 2 variables called c_add (stores correct answer) and add_rounds (stores total rounds/questions involving addition) as follows:
modes = ['Addition', 'Subtraction', 'Multiplication', 'Division']
def mode_var_names(mode):
return "mode_" + mode.lower() + "()", "c_" + mode.lower()[0:3], mode.lower()[0:3] + "_rounds"
roundmode = random.choice(modes)
mode_info = mode_var_names(roundmode)
the return keyword returns a string containing the variable name which
is then incremented through exec function
ex: exec(mode_info[1] + " += 1") increments the c_add if mode is
addition
I broke up the app into 2 main functions, the main one called mental_math (which contains the above code and generates the variables) and another one called results_page (which is called once I type in 'stop' at the interpreter or solve 20 questions) to show the percentage and how many questions of each mode have i got correctly.
Now the problem:
I am trying to get the variables generated at the mental_math to print their values at the results_page through the following code :
for i in modes:
print("In", i, "You got ", eval(mode_var_names(i)[1]), "correct answers out of ",
eval(mode_var_names(i)[2]))
And both the correct and the total of all the round return 0:
In Addition You got 0 correct answers out of 0
In Subtraction You got 0 correct answers out of 0
In Multiplication You got 0 correct answers out of 0
In Division You got 0 correct answers out of 0
, while the percentage "mark = round(correct / Round * 100)" is correct. When i copy the print loop back to the mental_math i get the correct numbers ( but still i want to know why the original state doesn't work)
What i tried so far
the global keyword thrown around is a failed attempt to get the results_page to get the modes' variables (but do the job for Round and correct variables), and i can't pass the eval() code used to generate the variables' names directly to global , i thought of using return method but don't know how to use it in this context (and would rather just make the generated variables accessible)
I also tried grouping them together as a class hoping all variables would be accessible to other functions or an instance but failed because i am just starting to learn OOP
Update & Solution:
There are (at least) 2 fixes, the answer Jason Nick Porter, and adding the 2 functions to the same class
Note: I ended up editing this a lot, so it gets increasingly correct as you go along :)
I would start by ditching the globals at the top. Global variables are a fickle mistress, one that I try to avoid at all costs. This would be a great place to use an array in your mental_math() fcn. Inside the function where you currently have:
global Round, correct, total_time, c_add, c_sub, c_mul, c_div, add_rounds, sub_rounds, mul_rounds, div_rounds
Round, total_time = 1, 0
correct, c_add, c_sub, c_mul, c_div, add_rounds, sub_rounds, mul_rounds, div_rounds = int(), int(), int(), int(), int(), int(), int(), int(), int()
I would just put:
level, total_time = [1,0] # I put level instead of Round, since round is a keyword and could be confusing
correct = [0,0,0,0] # the index here corresponds to the mode
asked = [0,0,0,0] # numpy.zeros would be an even more robust way to do this
So that each variable exists within the function, and then at the end of mental_math() have something like this:
if user == 'stop' or level == 20:
stats = [level, correct, total_time ] # and all the rest...
result_page(stats)
break
Then all the stats you have are passed to the results_page(). Now you'll want to make the modes into tuples instead of strings at the top of your program. Tuples are basically the best, because you can link together different types of variables, like so:
modes = [('Addition' , 3 , 7), # These numbers correspond to where the correct
('Subtraction' , 4 , 8), # and total numbers will be located in the stats
('Multiplication' , 5 , 9), # array.
('Division' , 6 , 10)] # The spacing just makes it easier to see
Now you'll have to adjust results_page() to receive the array. Then instead of each variable name, you can access all the values by their index. Like so:
def result_page(stats):
mark = round(correct / stats[0] * 100)
os.system('cls')
print("Mental math game")
print('FIN\n\n')
print("You solved {} questions".format(stats[0]))
print("You got {}% of the questions correct.".format(mark))
print("If you want detailed results type 'details'")
user = input("If you want to continue, type 'again', else just close this window or press Enter")
# Fun-fact: If you pass a string to input() it prints it as a prompt
if user == "details":
for i in modes:
print("In", i[0], " you got ", stats[i[1]], " correct answers out of ",
stats[i[2]])
Then you don't even need mode_var_names().
EDIT:
Ok I didn't realize some of the problems with my code, so some of the above is not great. I'm leaving it there in case it's helpful though.
The code below is updated and should let you add as many modes as you want. The problem was that I didn't look closely at mental_math(), so I didn't realize it was still calling mode_var_names(). Now instead of executing a function with a variable string, it just passes an int to a single function, and uses the same int to decide which numbers to update in the "correct" and "asked" arrays.
import os
import random
import numpy as np
modes = [('Addition' , 3 , 7), # These numbers correspond to where the correct
('Subtraction' , 4 , 8), # and total numbers will be located in the stats
('Multiplication' , 5 , 9), # array.
('Division' , 6 , 10)] # The spacing just makes it easier to see
def process(mode):
# if you have more modes you want to add, just add more elif statements
if mode == 0:
num1, num2 = random.randrange(1, 10), random.randrange(1, 10)
print(num1, " + ", num2)
return num1 + num2
elif mode == 1:
num1, num2 = random.randrange(1, 10), random.randrange(1, 10)
while num1 <= num2:
num1, num2 = random.randrange(1, 10), random.randrange(1, 10)
print(num1, " - ", num2)
return num1 - num2
elif mode == 2:
num1, num2 = random.randrange(1, 10), random.randrange(1, 10)
print(num1, " X ", num2)
return num1 * num2
elif mode == 3:
num2, num3 = random.randrange(1, 10), random.randrange(1, 10)
num1 = num2 * num3
print(num1, " ÷ ", num2)
return num3
def mental_math():
print("Mental math game: ")
level, total_time = 0,0 # I put level instead of level, since level is a keyword and could be confusing
correct = np.zeros(len(modes)) # the index here corresponds to the mode
asked = np.zeros(len(modes))
while level <= 20:
level += 1
print('Round ' + str(level), end=":\n\n")
m = random.choice(range(len(modes)))
print("This level will be about:", modes[m][0])
correct_answer = process(m)
asked[m] += 1
levelanswer = int(input("The answer is: "))
if levelanswer == correct_answer:
print("The answer {} is correct.".format(levelanswer))
correct[m] += 1
else:
print("The answer {} is incorrect. The answer should be: {}".format(levelanswer, correct_answer))
print("You got {} out of {} questions correct".format(sum(correct), level))
user = input("Press enter to continue, or type 'stop' to end this quiz\n")
if user == 'stop' or level == 20:
stats = np.hstack((level,sum(correct),total_time,correct,asked))
result_page(stats)
break
def result_page(stats):
mark = round(stats[1] / stats[0] * 100)
os.system('cls')
print("Mental math game")
print('FIN\n\n')
print("You solved {} questions".format(stats[0]))
print("You got {}% of the questions correct.".format(mark))
print("If you want detailed results type 'details'")
user = input("If you want to continue, type 'again', else just close this window or press Enter\n")
if user == "details":
for i in modes:
print("In", i[0], " you got ", stats[i[1]], " correct answers out of ",
stats[i[2]])
mental_math()