I'm learning Python and trying to build code where the for loop checks if a string is empty or non-numeric before it proceeds to add everything. How do I prevent it from going there and instead instruct it to loop through b to e?
3 Answers
There are several mistakes in your code:
- Have you realized that a, b, c, d, e do not exist inside your function? you're working with a list now, not with the individual variables that only exist outside your function! Accumulate the result of the sum in a variable instead, and print it outside the loop.
- To compare a value against
Noneuseis, not==. - The validation for checking if there are duplicates should be done outside the loop.
- You must not return on each condition! wait until the loop is over before returning.
Comments
if i == None: - this condition will not work if variable == ''
I replaced it with if not all(tuple(map(bool, chocolate))) - it is going to make all empty elements in list bool type. If element is empty it will be False else True. all() function checks if all the elements in list are True - it will return True.
Also i replaced this elif i.isnumeric() == False:, with this not all(tuple(map(str.isdigit, chocolate))), this code will use for each element method isdigit, works like previous one.
So then i think this elif len(chocolate) != len(set(chocolate)): part of your code is quite ok
Instead of this long code billday = int(a) + int(b) + int(c) + int(d) + int(e), you can use sum() function: billday = int(a) + int(b) + int(c) + int(d) + int(e)
And the last one i replaced code in else with f string: f"Your bill for today is {billday}. The total bill for this week is, {5 * billday}."
This is the final code:
b = input("Please input the price of the 2nd chocolate:")
c = input("Please input the price of the 3rd chocolate:")
d = input("Please input the price of the 4th chocolate:")
e = input("Please input the price of the 5th chocolate:")
chocolate = [a, b, c, d, e]
def chocolatebill(chocolate):
if not all(tuple(map(bool, chocolate))) or not all(tuple(map(str.isdigit, chocolate))):
return "You missed a price. Go back and input each price again."
elif len(chocolate) != len(set(chocolate)):
return "Double check your input. The price of each chocolate must be different."
else:
billday = sum(map(int, chocolate))
return f"Your bill for today is {billday}. The total bill for this week is, {5 * billday}."
print(chocolatebill(chocolate))```
Comments
You should check your inputs and then compute the result. These are two separate concerns your code has to handle. Do not do these different operations simultaneously. In your case you try return the sum after having checked only the first element of chocolate. Of course this fails if your second element already is e.g. None.
You should also take care about your variable naming. In Python, everything you define outside the function is also known inside the function scope:
x = 1
def f(y):
print(x) # x is known here, will print 1
print(y) # will print 2
f(y=2)
Hence
chocolate = [...]
def f(chocolate): # overwrites the `chocolate` variable defined previously
print(chocolate) # will print 1 instead of the list
f(chocolate=1)
Anyhow, to address your example, you should to something like the following:
A = input("Please input the price of the first chocolate:")
...
E = input("Please input the price of the 5th chocolate:")
CHOCOLATE = [A, B, C, D, E]
# naming convention: global variables, i.e. such declared outside a function/class scope
# should be capitalized
def chocolatebill(chocolates):
_validate_input(chocolates)
bill_day = sum(map(int, chocolates))
bill_week = 5 * bill_day
return f"Your bill for today is {bill_day}. The total bill for this week is {bill_week}."
def _validate_input(chocolates):
if len(chocolates) != len(set(chocolates)):
# this does not need to be checked every time in the for loop
raise Exception("Double check your input. The price of each chocolate must be different.")
for i in chocolates:
if not i: # checks if the input is an empty string (evaluates to True if empty)
raise Exception("You missed a price. Go back and input each price again.")
elif not i.isnumeric(): # instead of `... == False`
raise Exception("You missed a price. Go back and input each price again.")
print(chocolatebill(CHOCOLATE))
A few points here:
- No input via the
inputmethod always results in an empty string (''), never inNone - Instead of returning stuff in the validation, raise an
Exception. Ideally you even want dedicated exceptions, e.g.InvalidPriceErrorlike
This makes your code much more expressive.class InvalidPriceError(Exception): pass raise InvalidPriceError("You missed a price. Go back and input each price again.") sum(map(int, chocolates))is valid for an arbitrary number of chocolate prices. Heresumreturns the sum of elements in a a list-like element.map(int, chocolates)here maps theintmethod onto thechocolateslist, i.e. trys to apply it to each element and returns a list of the result (i.e. a generator to be precise).- I used an underscore before the
_validate_inputfunction because it is supposed to be a "private" method. I.e. it is supposed to be only used inside the scope of thechocolatebillfunction. This is a Python naming. convention.
There is even a lot more to mention here, but I don't want to stress you with too much information. I think this is already enough.
fordoesreturnon the first item in every single case. There is no way for any further items to be inspected.