Using exception handling inside a loop in python, I'm receiving inputs from a user and adds it to a list but if the user types 'done' the loop terminates and sums up the list. If the user types any other non numeric data it would print 'Wrong Data' and continue the loop. My issues are: Adding the list. Converting the user data from number to string. Reading a string data first from the user. And terminating the loop with 'done'.
total = 0
user_list = []
while True:
try:
user_entry = int(input('(\'done\' is your terminator.)\nEnter any number only! >> '))
user_list.append(user_entry)
total = total + user_list
except ValueError:
if str(user_entry) == 'done':
break
else:
print('Wrong Data')
continue
There's lots of code in the try block that should go before or after the try statement.
total = 0
user_list = []
while True:
user_entry = input('(\'done\' is your terminator.)\nEnter any number only! >> ')
if user_entry == 'done':
break
try:
user_entry = int(user_entry)
except ValueError:
print('Wrong Data')
continue
user_list.append(user_entry)
total += user_entry
Note that instead of keeping running total, you can simply wait until after the loop to call total = sum(user_list).
Related
I am trying to unit test my program and have decided to unit test the withdraw_cash function. However, it is called by the bank_atm function. I have never called a function that is dependent on another function and am confused about how to do this. Would i use mock and patch to do this?
The tests would be:
check whether entering valid amount
check whether amount is less than balance
user = {
'pin': 1234,
'balance': 100
}
def withdraw_cash():
while True:
try:
amount = int(input("Enter the amount of money you want to withdraw: "))
except ValueError as v:
print(f"Enter correct amount: ")
else:
if amount > user['balance']:
raise ValueError("You don't have sufficient balance to make this withdrawal")
else:
user['balance'] = user['balance'] - amount
print(f"£{user['balance']}")
return
finally:
print("Program executed")
def bank_atm():
count = 0
to_exit = False
while (count < 3) and (not to_exit):
try:
pin = int(input('Please enter your four digit pin: '))
except ValueError:
print("Please enter correct pin")
count += 1
if pin != user['pin']:
print("Pin does not match.. Try Again")
count += 1
else:
withdraw_cash()
to_exit = True
if count == 3:
print('3 UNSUCCESFUL PIN ATTEMPTS, EXITING')
print('!!!!!YOUR CARD HAS BEEN LOCKED!!!!!')
try:
bank_atm()
except ValueError as v:
print(f"ERROR: {v}")
To expand on the comments. Put UI interactions in separate functions, such as getpin and getpounds. They can then be tested separately from the business functions. One can either mix manual input with the unittests or automate them by patching sys.stdin/out. My proof-of-concept experiment, which passes on 3.11.0b3 run from IDLE editor.
import sys
import unittest
from unittest.mock import patch
def getpin(realpin):
return input('Your pin? ') == realpin
class ManualTest(unittest.TestCase):
def test_pin(self):
print('\n(Enter 1234)')
self.assertEqual(getpin('1234'), True)
print('\n(Enter 1)')
self.assertEqual(getpin('1234'), False)
class MockOut:
def write(self, string): pass
# Or 'write = Mock()' to save and check prompts, using mockout.
class MockIn:
def readline(self): # input() uses readline, not read.
return self.line
#patch('sys.stdin', new_callable=MockIn)
#patch('sys.stdout', new_callable=MockOut)
class AutoTest(unittest.TestCase):
def test_pin(self, mockout, mockin):
mockin.line='1234\n' # input removes '\n' if present.
self.assertEqual(getpin('1234'), True)
mockin.line='1233'
self.assertEqual(getpin('1234'), False)
if __name__ == '__main__':
unittest.main(verbosity=2)
I have now refactored my program, by breaking it down into separate functions as you said but when I try and run a test specifically for one function, it runs the whole thing and asks for input from each function. I feel like testing each function individually will have to require specific parameters not dependent on the other function.
user = {
'pin': 1234
}
def withdraw_cash(amount):
balance_account = 100
if amount > balance_account:
raise ValueError("You don't have sufficient balance to make this withdrawal")
else:
new_balance = balance_account - amount
return new_balance
def get_pin():
count = 0
to_exit = False
while (count < 3) and (not to_exit):
try:
pin = int(input('Please enter your four digit pin: '))
except ValueError:
print("Please enter correct pin")
count += 1
if pin != user['pin']:
print("Pin does not match.. Try Again")
count += 1
else:
return get_amount(pin)
#return pin
if count == 3:
a = '3 UNSUCCESFUL PIN ATTEMPTS, EXITING \n !!!!!YOUR CARD HAS BEEN LOCKED!!!!!'
return a
def get_amount(pin):
while True:
try:
amount = int(input("Enter the amount of money you want to withdraw: "))
except ValueError as v:
print(f"Enter correct amount: ")
else:
#print(amount)
return withdraw_cash(amount)
#return amount
try:
get_pin()
except ValueError as v:
print(f"ERROR: {v}")
lst = []
while True:
try:
arr = int(input("Enter number of elements: "))
if arr == "Quit":
break
except ValueError:
print("Invalid Input")
continue
else:
break
while True:
try:
for i in range(0, arr):
ele = int(input("Enter the elements:"))
lst.append(ele)
print(lst)
except ValueError:
print("Invalid Input")
continue
else:
break
How can I create a condition to exit the program at any point in the loop by specifically entering the conditioned term?
Like when it asks me to enter an element, but i want to break the program right at that point by entering
"Quit".
How to do that in this code?
(Learner)
Use sys.exit() to exit the program completely. Use break to exit the specific loop you're in (which you've used).
Other notes:
You're doing arr = int(input(...)) but you want to accept the input of "Quit" which is a string. So if the user enters "Quit", it raise ValueError and the loop continues. So check for "Quit" first and if it's not quit, then convert to int with the try-block
Same applies for the 2nd loop where you ask the user for the elements in the list
Btw, your second loop's while True loop should be inside the for-loop which gets each element.
lst = []
while True:
arr = input("Enter number of elements: ")
if arr == "Quit":
sys.exit() # will exit completely
try:
arr = int(arr) # check for int in the try-block, error raised here, if not int
break # can put break here instead of in else, any is okay
except ValueError:
print("Invalid Input")
# continue not needed here, since it loops infinitely by default
for i in range(0, arr):
while True:
ele = input("Enter the elements:")
if ele == "Quit":
sys.exit() # will exit completely
try:
ele = int(ele) # check for int in the try-block, error raised here, if not int
lst.append(ele)
break # breaks out of `while` loop, not `for` loop; good
except ValueError:
print("Invalid Input")
# continue not needed here, since it loops infinitely by default
print(lst) # print the full list after all the inputs
The pattern for your input is repeated:
Ask user for input
if input is "Quit", then exit completely
otherwise, convert the input to an int
So this can put into a function, which you call in both places:
def int_or_quit(msg):
"""`msg` is the message you want to show at input"""
while True:
item = input(msg)
if item == "Quit":
sys.quit() # will exit completely
try:
return int(item) # try converting to int and return
except ValueError:
print("Invalid Input")
# repeats by default
# use the function above in your two code blocks, which are now simplified:
lst = []
arr = int_or_quit("Enter number of elements: ")
# program exits before if user "Quit", next part won't execute
for i in range(0, arr):
ele = int_or_quit("Enter an element:")
lst.append(ele)
print(lst)
I can see that there're 2 blocks in your code snippet. I'd suggest organising them in distinct functions, where function name infers a function goal (instead of comments):
def get_list_size():...
def fill_list():...
Next thing to do is deciding about function's return values:
from typing import List, Optional
def get_list_size() -> Optional[int]:
"""
:returns: number of desired elements of list or None, if "Quit" was pressed
:raises: ValueError exception, if non integer value was pressed
"""
def fill_list(lst_size: int) -> List[int]:
"""
returns: filled list
throws: ValueError exception, if non integer value was pressed
"""
Here you can see, how Optional type hint is used to add condition logic into the program.
Lats thing to do is to fill your functions with lightweight code:
def get_list_size() -> Optional[int]:
"""
:returns: number of desired elements of list or None, if "Quit" was pressed
:raises: ValueError exception, if non integer value was pressed
"""
while True:
try:
input_value = input("Enter number of elements: ")
# Pay attention: you have to check string input_value, not int(input_value).
# Checking int(input_value) would raise exception before 'Quit' validation.
if input_value == "Quit":
return None
return int(input_value)
# Could be rewritten as trinary if
# return None if input_value == "Quit" else int(input_value)
except ValueError:
print("Invalid Input")
# continue <- Not needed here
def fill_list(lst_size: int) -> List[int]:
"""
returns: filled list
throws: ValueError exception, if non integer value was pressed
"""
lst = []
while len(lst) < lst_size:
try:
ele = int(input("Enter the elements:"))
lst.append(ele)
print(lst)
except ValueError:
print("Invalid Input")
return lst
Pay attention: in fill_list function you have to try/except each element, so while loop was moved outside.
Last thing to do is running these functions:
desired_list = []
desired_list_size = get_list_size()
if desired_list_size is not None:
desired_list = fill_list(desired_list_size)
def one():
message = ""
while message != "Quit":
message = input("Type in Order > ")
if message == "Run":
print("Run some Code")
message = input("What do u want to run ? > ")
if message == "Exel":
print("Runing Exel")
else:
pass
else:
pass
while True:
one()
Here you can pass in funktions that will run when you call it by its Name. If u write Quit it will start over again. It starts over again if you write something wrong. U could do an FailExeption for that if u want to.
my task is to calculate the amount of money in a saving account in two ways and compare the results. It prompt the user for input principle, the interest rate(as a percent), and years of invest.I need to use try-except block to validate the input, and use a while statement to prompt the user until a valid input. I have issue on the validation and while process. When I had invalid input, it didn't print associated exception error as expected.The function parts are Ok, just ignore them. Also, "Going around again" is supposed to print before the next prompt input, but mine appeared by the end of correct input execution. Could you please help me? Thanks.
def calculate_compound_interest(principle, int_rate, years):
value = principle * (1 + int_rate)**years
return value
def calculate_compound_interest_recursive(principle, int_rate, years):
if years == 0:
return principle
else:
recursive_value = calculate_compound_interest_recursive(principle, int_rate, years-1)*
(1+int_rate)
return recursive_value
def format_string_output(value, recursive_value):
return "Interest calculated recursively is {:,.2f} and calculated by original formula is
{:,.2f}.These values are a match.".format(recursive_value,value)
print(__name__)
if __name__ == "__main__":
while True:
principle_input = input("Please input principle:")
interest_rate_input = input("Please input interest rate with %:")
years_input = input("Please input years:")
try:
p = float(principle_input)
i = (float(interest_rate_input.replace("%","")))/100
n = int(years_input)
except ValueError():
print("Error: invalid principle.")
except ValueError():
print("Error: invalid interest rate.")
except ValueError():
print("Error: invalid years.")
else:
print(calculate_compound_interest(p, i, n))
print(calculate_compound_interest_recursive(p, i, n))
print(format_string_output(calculate_compound_interest(p, i, n),
calculate_compound_interest_recursive(p, i, n)))
break
finally:
print("Going around again!")
Note: Finally block runs whenever a try or any except block runs.
The Try-Except blocks need to be paired up, easier to show than explain.
def calculate_compound_interest(principle, int_rate, years):
value = principle * (1 + int_rate)**years
return value
def calculate_compound_interest_recursive(principle, int_rate, years):
if years == 0:
return principle
else:
recursive_value = calculate_compound_interest_recursive(principle, int_rate, years-1)*(1+int_rate)
return recursive_value
def format_string_output(value, recursive_value):
return "Interest calculated recursively is {:,.2f} and calculated by original formula is {:,.2f}.These values are a match.".format(recursive_value,value)
if __name__ == "__main__":
while True:
principle_input = input("Please input principle:")
interest_rate_input = input("Please input interest rate with %:")
years_input = input("Please input years:")
try:
p = float(principle_input)
except ValueError():
print("Error: invalid principle.")
print("Going around again!")
continue
try:
i = (float(interest_rate_input.replace("%","")))/100
except ValueError():
print("Error: invalid interest rate.")
print("Going around again!")
continue
try:
n = int(years_input)
except ValueError():
print("Error: invalid years.")
print("Going around again!")
continue
print(calculate_compound_interest(p, i, n))
print(calculate_compound_interest_recursive(p, i, n))
print(format_string_output(calculate_compound_interest(p, i, n),
calculate_compound_interest_recursive(p, i, n)))
break
Let me know any questions via a comment.
So I have an otherwise easy homework assignment that wants me to input my grades for subjects, etc. The amount of input varies with the number of subjects the user wants to enter. The input works fine, but when I print out the saved values only the ones most recently entered are saved. Here is my code for input:
def gpa_input(classList):
print("please enter how many classes you have")
while True:
try:
numOfClasses = int(input())
except ValueError:
print("please enter a valid number")
continue
else:
break
for i in range (numOfClasses):
classList.append(subject)
print("enter name of " + str(i+1) + "th subject:")
classList[i].name = input()
print("enter num of credits for " + str(i+1) + "th subject:")
while True:
try:
classList[i].credits = int(input())
except ValueError:
print("please enter a valid number")
continue
else:
break
print("enter grade for " + str(i+1) + "th subject:")
while True:
try:
classList[i].gradePercentage = int(input())
except ValueError:
print("please enter a valid number")
continue
else:
break
A subject is a class containing a string value and 2 int values, defined as following:
class subject:
def __init__(name, credits, gradePercentage):
self.name = name
self.credits = credits
self.gradePercentage = gradePercentage
And here is the code that prints out all of this:
def main():
gpa_input(classList)
for i in range (len(classList)):
print(classList[i].name)
print(classList[i].credits)
print(classList[i].gradePercentage)
What is the problem with my code? Am I iterating through something the wrong way, or is there something not getting properly assigned/saved?
You've got into the very common "trap".
The problem is in how you initialize your subject class.
Here you just append a class to the list:
classList.append(subject)
The situation here is the following:
Once you called subject without braces you will have a new object.
But when you call it on the second time - python will not initialize the new object for you and just return the object created on the first step.
So all you need is to properly initialize all subject objects.
Two ways:
1) Remove args from subject definition and make default values are none + add braces to the classList.append(subject)
2) Collect all values in your for loop into variables and at the end of the function initialize you subject class with proper args.
Hi guys I was working on a shoppinglist-creator code but at the end I faced with a surprise.
My code:
import time
import math
import random
dict_of_lists={}
def addlist():
while True:
try:
listname=str(raw_input("=>Name of the list:\n"))
dict_of_lists[listname]={}
break
except ValueError:
print "=>Please enter a valid name.\n"
print "=>You added a new list named %s.\n" % (listname)
def printlists():
for lists in dict_of_lists:
return "-"+lists
def addproduct():
while True:
try:
reachlistname=input("=>Name of the list you want to add a product,Available lists are these:\n %s \nPlease enter one:\n" % (printlists()))
break
except ValueError:
print "=>Please enter a valid list name.\n"
while True:
try:
productname=raw_input("=>Name of the product:\n")
break
except ValueError:
print "=>Please enter a valid name.\n"
while True:
try:
productprice=input("=>Price of the product:\n")
if isinstance(float(productprice),float):
break
except ValueError:
print "=>Please enter a valid number.\n"
while True:
try:
productcount=input("=>Amount of the product:\n")
if isinstance(int(productcount),int):
break
except ValueError:
print "=>Please enter a valid number.\n"
dict_of_lists[reachlistname][productname]={"price":productprice,"count":productcount}
dict_of_lists[reachlistname]={productname:{"price":productprice,"count":productcount}}
allevents="1-Add a list"+" 2-Add a product to a list"
def eventtochoose():
while True:
try:
event=raw_input("=>What would you like to do? Here are the all things you can do:\n %s\nPlease enter the number before the thing you want to do:" % (allevents))
if not isinstance(int(event),int):
print "\n=>Please enter a number.\n"
else:
if event==1:
addlist()
break
elif event==2:
addproduct()
break
except ValueError:
print "\n=>Please enter a valid input.\n "
while True:
print "%s" % ("\n"*100)
eventtochoose()
So, the problem is (I suggest you run the code) it says "=>What would you like to do? Here are the all things you can do:
1-Add a list 2-Add a product to a list
Please enter the number before the thing you want to do:" and when i put an answer it simply doesn't call the fucntion.
If I put 1 It should have called the fucntion addlist but I think it doesn't. There is nothing to explain I think just look at the code and find the problem if you want to help crocodiles. Thx
When you do int(event), that returns an int if possible, and raises a ValueError if not. So, testing the type of the result doesn't do you any good—if your code gets that far, the type has to be an int.
You already have code to handle the ValueError, so you don't need any other test for the same problem.
Meanwhile, you want to start the number that you got from int(event). That's the thing that can be == 1; the original string '1' will never be == 1.
So:
while True:
try:
event=raw_input("=>What would you like to do? Here are the all things you can do:\n %s\nPlease enter the number before the thing you want to do:" % (allevents))
event = int(event)
if event==1:
addlist()
break
elif event==2:
addproduct()
break
except ValueError:
print "\n=>Please enter a valid input.\n "
You are not converting your input to an integer before comparing, so the comparisons are always false:
'1' == 1 # false
Try:
event = raw_input("=>What would you like to do? Here are the all things you can do:\n %s\nPlease enter the number before the thing you want to do:" % (allevents))
try:
event = int(event)
if event == 1:
addlist()
elif event == 2:
addproduct()
break
except ValueError:
print('Please enter a valid input')