The program I'm writing is to use a given formula to calculate financial assistance for families that loops back and asks the user if they want to do it for another family. Part of the instructs was also to use -1 as a sentinel value for the input. Here is what I have at the moment:
def main():
cont = "y"
while cont.lower() == "y":
try:
income = float(input("Please enter the annual household income:"))
children = int(input("How many children live in the house?"))
amountAssistance = assistanceTotal(income, children)
print("For a household making", income, "per year with", children, " children the amount of assistance would be", amountAssistance)
cont = input("Would you like to run this again? (Y/N)")
except:
print("Something went wrong.")
print("Did you enter a whole number of children?")
cont = input("Would you like to try again? (Y/N)")
def assistanceTotal(money, kids):
if (money >= 30000 and money <= 40000) and kids >= 3:
moneyTotal = 1000 * kids
return moneyTotal
if (money >= 20000 and money <= 30000) and kids >= 2:
moneyTotal = 1500 * kids
return moneyTotal
if money < 20000:
moneyTotal = 2000 * kids
return moneyTotal
main()
I've tried looking up info on using sentinel values with loops like this but I haven't been able to quite grasp if how I'm thinking of using it is correct. For this type of a situation would I need one sentinel value or two, one for income and one for children? I think I would also need to alter my cont statements and change how it reacts if they type in a -1. Any help is appreciated even if it is just directing me to accurate info on how to use sentinels.
Edit to add another sort of follow up question:
Do sentinel values have to be numbers or are they most commonly numbers? What is the difference between having a continue portion using yes/no inputs to using sentinel values? For an example: In my text book it shows using a sentinel to stop the loop by asking for either an input of an integer or enter -1 to exit, is this not the same thing as asking continue Y/N?
If you want all of the inputs to be able to detect the sentinel value, you can use a custom input function that raises a RuntimeError (or a custom exception of your own) and handle the exception in a uniform way.
def main():
def get_input(prompt):
i = input(prompt)
if i == '-1':
raise RuntimeError()
return i
cont = "y"
while cont.lower() == "y":
try:
income = float(get_input("Please enter the annual household income:"))
children = int(get_input("How many children live in the house?"))
amountAssistance = assistanceTotal(income, children)
print("For a household making", income, "per year with", children, " children the amount of assistance would be", amountAssistance)
cont = input("Would you like to run this again? (Y/N)")
except RuntimeError:
print("Done.")
break
except:
print("Something went wrong.")
print("Did you enter a whole number of children?")
cont = input("Would you like to try again? (Y/N)")
Generally you would use the sentinel to exit the loop, in this case entering -1 for income, e.g.:
while True:
try:
income = int(input("Please enter the annual household income (-1 to exit):"))
if income == -1:
break
children = int(input("How many children live in the house?"))
amountAssistance = assistanceTotal(income, children)
print("For a household making", income, "per year with", children, " children the amount of assistance would be", amountAssistance)
except ValueError:
print("Something went wrong.")
print("Did you enter a whole number of children?")
NB 1: given the sentinel is -1 and all of the tests for income are against ints I would suggest keeping income an int.
NB 2: it is best to catch explicit exceptions, e.g. ValueError vs a catch-all except: clause
Related
I have made a program for an ATM machine, which checks the pin that a user inputs, allows the user to enter an amount to withdraw if the pin matches and then withdraws the money from the account_balance. However, each function is seemingly dependent: the return of one function is the parameter of another. This is confusing to test each function separately and when I try to do so, the whole program runs and asks for input from each function.
As my get_pin and get_amount function also have while loops with user input, I feel like this has added another complication to the way I need to unit test.
Is there a way to do this?
Or do you think it is best to refactor my code to remove user input and while loops and just have parameters in each individual function?
The test I would like to check would be:
withdraw_cash function - to check if the correct amount is withdrawn
get_pin function - check if the pin matches
get_amount function - check incorrect amount is added bringing up an error message.
My atm program is:
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)
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:
return withdraw_cash(amount)
try:
get_pin()
except ValueError as v:
print(f"ERROR: {v}")
store={'Rice':450,'Beans':200,'Egg':40,'Fish':250,'Spag':250}
bill=()
total=()
print('Welcome!!!we sell:',"\n",[store])
while True:
a=input('What would you like to buy?=')
b=input('how many of each product do you want?=')
if a in store:
bill=store[a]*b
print('bill=',bill)
elif a not in store:
print('Sorry we don\'t have that')
else:
total=bill+total
print('Total=',total)
Your if/elif/else is impossible, because wether a is IN the dict, wether it isn't, there is no way you into the else so put it in the if.
I'd also added a way to stop the loop by entering stop without you won't be able to stop the program and print the final total
move b input into the if, that isn't necessary to ask for quantity if the product isn't available
added int() conversion on b
store = {'Rice': 450, 'Beans': 200, 'Egg': 40, 'Fish': 250, 'Spag': 250}
print('Welcome!!!we sell:', "\n", store)
total, bill = 0, 0
while True:
a = input('What would you like to buy? ("stop" to quit): ')
print(">" + a + "<") # For OP debug purpose only, to be removed
if a == "stop":
break
if a in store:
b = int(input('how many of each product do you want?='))
bill = store[a] * b
total += bill
print(f"Total={total} (+{bill})")
else:
print('Sorry we don\'t have that')
print('Total=', total)
I'm new here and I have a problem with too much if, else statement in while loop. I want to refactor it to function, but I don't have any idea how to do it.
My code:
brand = input("Please select a brand...")
if brand.lower() == "XX" or sex == "1":
print("You selected a XX...")
while True:
product = input()
if product.lower() == "apple" or product == "1":
print("You selected Apples!\n")
while True:
size_schema = input()
if size_schema.lower() == "in" or size_schema.lower() == "inch" or size_schema == "1":
while True:
apple_size = float(input())
if 8.5 <= apple_size <= 12.0:
real_apple_size = round(apple_size, 2)
print("Your apple size is {} inch!".format(real_apple_size))
cursor = size_guide.find({})
for document in cursor:
a = document['Product']['Apple']['INCH']
try:
b = [float(x) for x in a if x != '']
result = min(enumerate(b), key=lambda i: abs(i[1] -
float(real_apple_size)))
c = str(result[1])
except ValueError:
pass
real_apple_size = str(real_apple_size)
if real_apple_size in document['Product']['Apple']['INCH']:
index = document['Product']['Apple']['INCH'].index(real_apple_size)
print("We have this apples from {} brand!"
.format(document['Brand']))
elif c in document['Product']['Apple']['INCH']:
last_list_value = next(s for s in reversed(a) if s)
index = document['Product']['Apple']['INCH'].index(c)
real_apple_size = float(real_apple_size)
print("SORRY! We don't have exactly your size, "
"but we have similar size from {} brand!"
.format(document['Brand']))
else:
print("Sorry, We don't have apples for you from {} brand!"
"Check our other products!"
.format(document['Brand']))
else:
print("Please select your apple size in range 8.5-12.0 inch!")
continue
break
I want to reduce this code and insert it in function.
Better (though probably not best) functional code would be a set of functions that are reusable, and each do one (or a very small number) of things. For example:
def get_product():
brand=input("What brand?")
#input validation logic
product=input("What product?")
#input validation for product given brand
size=input("What size?")
#input validation given brand and product
color=input("What color? (enter 'none' for no color)")
#That's right, more validation
return brand, prod, size, color
def prod_lookup(brand, prod, size, color):
cursor = size_guide.find({})
for document in cursor:
#lookup product with logic as in your post
if __name__ == "__main__":
brand, prod, size, color = get_product()
prod_lookup(brand, prod, size, color)
Again, this is just an example of one way to do it that would be much less messy. If you need to update your list of available products, for example, you only have to adjust one part of one function, rather than choosing from a deeply nested bunch of conditionals and loops.
I'm sure there are better ways, but hopefully this gives you some idea where to start thinking.
Adding one possible implementation of input validation with product lookup. This way, your brand will always be the product number rather than the string, which is usually a faster lookup:
brand_dict={'xx':'1','yy':'2'}
while True:
brand=input("Enter brand: ").lower()
if brand in brand_dict.keys():
brand=int(brand_dict[brand])
break
elif brand in brand_dict.values():
brand=int(brand)
break
else:
print("Brand not recognized. Try again!")
First, just wrap the whole thing in one function
def foo():
brand = input("Please select a brand...")
if brand.lower() == "XX" or sex == "1":
# etc.
Now, note that your first if statement encompasses the rest of the function, and there is no else clause. That is, if the condition fails, you'll fall through to the end of the function and implicitly return. So just return explicitly if the condition does not* hold. This lets you immediately dedent the bulk of your code.
def foo():
brand = input("Please select a brand...")
if brand.lower() != "XX" and sex != "1":
return
print("You selected a XX...")
# etc
Repeat this process, either returning or breaking out of the enclosing infinite loop, for each of your else-less if statements.
My name is Jamie, I'm a Yr 12 student currently living in NZ. At school, in our computer science class we were tasked with creating a program for house points. A house is sort of like the Houses in Harry Potter, each student is assigned to one. These houses then compete in events and earn points, at the end of the year the house with the most points wins the trophy.
Now we have only been taught about 2-D arrays and parallel lists, as these need to be incorporated plus it must be modular.
The program must be fully user inputed as requirements for excellence (equivalent of A) must be user inputed.
The program must also have these inputs and outputs:
Inputs: House Names, House Events, and Points earned in events for the house
Cancel house name and house event entry when XXX is entered.
Outputs: Winner of each event, house with best average, house with most wins, and overall winner.
I am currently trying to figure out how to do the points to go with house and events.
Appreciate all help,
Jamie :)
EDIT: Posted Code
def number_house():
global numhouse
print("Welcome!")
print()
Flag = True#loop
while Flag:
try:
numhouse = int(input("Please enter the number of events there is: "))
print()
if numhouse < 1 or numhouse > 100:
print("WOW, thats a lot of events, please be reasonable. Thanks.")
else:
Flag = False
except ValueError:
print("Enter only a number, Thanks.")
def event_names():
global event
print("Enter XXX when finished entering event names")
Flag = True
for e in range(numhouse):
e = input("Event name: ")
if e == 'XXX' or e == 'xxx':
Flag = False
else:
event.append(e)
print()
def getData():
global data
global inputlist
global event
lower_bound = 0
upper_bound = 100
k=0
n=str(input("Please enter house names <<<Enter XXX when finished>>> :"))
while n != 'XXX' :
if n == 'XXX' or n == 'xxx':
exit
data = [n]
print()
print ("Please enter the event points in ascending order. ",event,"Thanks")
for k in range(len(event)):
s = getScore(n,lower_bound,upper_bound)
data=data+[s]
inputlist = inputlist + [data]
n=str(input("Please enter house names <<<Enter XXX when finished>>> :"))
def getScore(name,min,max):
global event
sc= -1
while sc < min or sc > max :
try :
sc = int(input("Please enter score for "+ name + " :"))
except ValueError :
print("Invalid Input please enter an interger. Thanks")
return sc
score =[]
getscore = []
data = []
inputlist = []
event = []
number_house()
event_names()
getData()
print()
print(inputlist)
LOWER_BOUND = 0
UPPER_BOUND = 100 # both in caps because this is a constant
def get_score(house_name, event_name):
# an extra argument so you can tell which event is being scored.
# removed the min, max cause we are using the constants!
score = -1
while score < LOWER_BOUND or score > UPPER_BOUND:
try:
score = int(input("Please enter score for %s in the event %s:" % (house_name, event_name)))
if score < LOWER_BOUND :
print ("Score is too low, minimum score is %i.\nPlease try again." % min_score)
if score > UPPER_BOUND:
print ("Score is too high, maximum score is %i\nPlease try again." % max_score)
except ValueError:
print("Invalid Input please enter an integer. Thanks")
return score # note the use of return to avoid using global
def get_number_of_events():
print("Please enter the number of events there is.")
while True:
try:
n_events = int(input(">>"))
except ValueError:
print("Enter only a number, Thanks.")
if n_events > 100:
print("WOW, that's a lot of events, please be reasonable. Thanks.")
elif n_events < 1:
# this is a different condition that would get a wrong error in your program,
# note the use of 'elif', in Python this an 'else if'.
print ("That's too few events! Try Again.")
else:
# no need to use Flag, just use break when you want to leave a loop.
break
return n_events
def get_events_names(n_events):
print ("Please enter the events names")
events = []
for n in range(1, n_events + 1):
# starting at 1 to give a better display
event_name = input("Event %i name: " % n)
events.append(event_name)
return events
def get_data(events):
data = []
while True:
house_name = input("Please enter house names <<<Enter XXX when finished>>> :")
if house_name.upper() == "XXX":
# using .upper() to avoid checking twice for either 'xxx' or 'XXX'.
# I would ask the user for how many houses are there instead, but your choice ;)
break
print ("Please enter the events points in ascending order.")
# not actually need to be in ascending order, you can sort them later if you want.
scores = []
for event_name in events:
# don't use range(len(something)), loops in Python are easy!
score = get_score(house_name, event_name)
scores.append([event_name, score])
data.append([house_name, scores])
# use .append() instead of data = data + [...]
return data
def main():
print("Welcome!\n")
n_events = get_number_of_events()
events_names = get_events_names(n_events)
print()
data = get_data(events_names)
print()
for house_name, event_data in data:
print ("House " + house_name)
for event_name, score in event_data:
# note the use of tuple unpacking
print ("\tEvent: %s Score: %i" % (event_name, score))
if __name__ == '__main__':
main()
This time maintaining the same structure as your program.
Check comments for some tips and tricks.
Also, try to keep your variable names with meaning and check the PEP 8 guidelines for naming conventions (variables and functions should be snake_case,
Output:
Welcome!
Please enter the number of events there is.
>>2
Please enter the events names
Event 1 name: Quidditch Match
Event 2 name: Duels
Please enter house names <<<Enter XXX when finished>>> :Gryffindor
Please enter the events points in ascending order.
Please enter score for Gryffindor in the event Quidditch Match:100
Please enter score for Gryffindor in the event Duels:30
Please enter house names <<<Enter XXX when finished>>> :Slytherin
Please enter the events points in ascending order.
Please enter score for Slytherin in the event Quidditch Match:40
Please enter score for Slytherin in the event Duels:50
Please enter house names <<<Enter XXX when finished>>> :XXX
House Gryffindor
Event: Quidditch Match Score: 100
Event: Duels Score: 30
House Slytherin
Event: Quidditch Match Score: 40
Event: Duels Score: 50
I'll try to give you a different approach, see if that helps you.
def main():
events = []
houses = []
scores = {} # key = House name, value = scores
print "Welcome!\n"
# create the houses
print "Please enter how many houses there is:"
n_houses = int(raw_input(">>"))
print "Please enter houses names:"
for n in range(n_houses):
print "House", n+1
house_name = raw_input(">>")
houses.append(house_name)
# create the events
print "Please enter the number of events there is"
n_events = int(raw_input(">>"))
print "Please enter the event names"
for n in range(n_events):
print "Event", n+1
event_name = raw_input(">>")
events.append(event_name)
# get the scores for each house for each event
for event in events:
for house in houses:
print "Please enter the score for House %s in the event %s"%(house, event)
score = int(raw_input(">>"))
# initialize the score with a empty list
if house not in scores:
scores[house] = []
# add the score list
scores[house].append(score)
print "\nThe result is:"
# process the result
for house, score in sorted(scores.items(),
key=lambda x: sum(x[1]),
reverse=True):
print "House %s. Total Score: %i"%(house, sum(score))
if __name__ == "__main__":
main()
First thing you should notice is that I'm not using global, using global is usually frowned upon, it can lead to undesired interactions on data.
Also, instead of asking for inputs like "XXX" to break the loop, I asked the user for the number of inputs he wants to deal before, so I can loop over this number and process each separately.
I do the same thing with the house, I ask for how many houses are there, and then their names.
Next I do a nested for loop with the event names and house names. The order matters, we deal with each event first. You can change it to deal with each house first.
And finally I process the scores. the line for house, score in sorted(scores.items(), key=lambda x: sum(x[1]), reverse=True): is a bit clogged and advanced but it means this: I want to loop over a sorted list of items, giving me two items at a time, the items are named house and score, they will be sorted by the function sum(x[1]), and I want this in the reversed order (or else the last would show in first).
key=lambda x: sum(x[1]) is a bit of a hack, it could be done better. lambda means a function, it takes x as input, x in this case is a tuple of house, score, so I want the score, so I access it using x[1], and since I want the sum I use sum(x[1]).
Usage:
Welcome!
Please enter how many houses there is:
>>2
Please enter houses names:
House 1
>>Gryffindor
House 2
>>Slytherin
Please enter the number of events there is
>>2
Please enter the event names
Event 1
>>Quidditch Match
Event 2
>>Duels
Please enter the score for House Gryffindor in the event Quidditch Match
>>100
Please enter the score for House Slytherin in the event Quidditch Match
>>90
Please enter the score for House Gryffindor in the event Duels
>>250
Please enter the score for House Slytherin in the event Duels
>>240
The result is:
House Gryffindor. Total Score: 350
House Slytherin. Total Score: 330
Notice that this was made on Python 2.7, to port to Python 3 just change raw_input to input and print to print()
I have this code. If you run it everything works fine if you follow the instructions. However I want to dummyproof it but when you enter too make troops and then you fix your mistake you get an error after fixing the mistake when functions restarts itself.
Please take a look and help me fix it.
import time
warriors = 100
def deploy(): #Calls fighters to front lines
amount = input('How many warriors would you like to send to the front lines? Your limit is %i warriors. Keep in mind that enemy invaders have been spotted inside your base. You must keep 10 warriors in base at all times. ' %(warriors))
try:
amount = int(amount)
except ValueError:
print('Please use numbers.')
time.sleep(1.5)
deploy()
if amount <= warriors:
print (type(amount))
elif amount > warriors:
print("You can't send that many warriors. You only have %i warriors." %(warriors))
time.sleep(1.5)
amount=0
deploy()
else:
print("You did something wrong. Try again.")
time.sleep(1.5)
deploy()
fighters = deploy()
warriors = warriors - fighters
You shouldn't use recursion (e.g. a function calling itself repeatedly) to try and do validation. For some general examples of good patterns for this, the canonical question is a good start. In your case I might refactor slightly.
import time
warriors = 100
def deploy():
while True:
amount = input("...") # your text here
try:
amount = int(amount)
except ValueError:
print("Please use numbers.")
# move the time.sleep to the end
else: # only execute if the try block succeeds
if amount > warriors:
print("You can't send that many warriors. "
"You only have %i warriors." % warriors)
else:
# everything went right!
print(type(amount)) # why are you doing this...?
return amount # did you forget this in your sample code?
# if you get here: something broke
time.sleep(1.5)
That said, this is kind of ugly since it's so deeply nested. Remember the Zen: "Flat is better than nested." Let's refactor a bit to make a new function that does the validation for us.
import time
warriors = 100
def int_less_than(prompt, ceil, type_error_msg=None,
value_error_msg=None, callback=None):
"""Returns a validated integer
input(prompt) must be less than ceil. Print to console a std error msg
if none is specified. If you specify a callback: run the callback if any
errors are detected.
"""
while True:
user_in = input(prompt)
try:
user_in = int(user_in)
except ValueError:
print(type_error_msg or "You must enter a number")
else:
if user_in > ceil:
print(value_error_msg or "You must enter a number "
"less than {}".format(ceil))
else:
return user_in
if callback is not None:
callback() # lets us insert a time.sleep call
def deploy():
amount = int_less_than("How many warriors would you like to...",
warriors,
callback=lambda: time.sleep(1.5))
return amount