0

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?

4
  • 1
    It doesn't matter whether any of the other elements are empty or not – the for does return on the first item in every single case. There is no way for any further items to be inspected. Commented Sep 3, 2021 at 13:42
  • 1
    Make sure that you do all your checks THEN compute the total. You may also want to consider checking the user input directly as they give it instead of making them give them all first, then checking. Commented Sep 3, 2021 at 13:42
  • in input() never returns None - it may return an empty string "" - checking i== None does nothing Commented Sep 3, 2021 at 13:46
  • Thanks for all the help! I understand how I've over complicated it by combining everything into one code block. I'll separate it and work from there. Thank you! Commented Sep 4, 2021 at 0:09

3 Answers 3

1

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 None use is, 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.
Sign up to request clarification or add additional context in comments.

Comments

1

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

0

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 input method always results in an empty string (''), never in None
  • Instead of returning stuff in the validation, raise an Exception. Ideally you even want dedicated exceptions, e.g. InvalidPriceError like
    class InvalidPriceError(Exception):
        pass
    
    raise InvalidPriceError("You missed a price. Go back and input each price again.")
    
    This makes your code much more expressive.
  • sum(map(int, chocolates)) is valid for an arbitrary number of chocolate prices. Here sum returns the sum of elements in a a list-like element. map(int, chocolates) here maps the int method onto the chocolates list, 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_input function because it is supposed to be a "private" method. I.e. it is supposed to be only used inside the scope of the chocolatebill function. 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.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.