Abdul Hadi
Abdul Hadi

Reputation: 11

First if statement being ignored by python but not elif's

Beginner in python here. I'm trying to create a program that takes the takes the total price of many things and rounds them up or down depending on their last digit. My issue is that my first "if" statement always gets ignored but all my other "elif" statements work just fine

Code:

if str(finalTotalPrice).endswith("1" or "2") :
    roundedDown = round(finalTotalPrice, 1)
    print("Final Total = $" + str(roundedDown) + str(0))
    print()
    
    cashPayment = float( input("How much cash will you pay with? $"))
    change = (cashPayment - roundedDown)
    change = round(change, 3)
    print("Your change is $" + str(change))

elif str(finalTotalPrice).endswith("8" or "9") :
    roundedUp = round(finalTotalPrice, 1)
    print("Final Total = $" + str(roundedUp) + str(0))
    print()

    cashPayment = float( input("How much cash will you pay with? $"))
    change = (cashPayment - roundedUp)
    change = round(change, 3)
    print("Your change is $" + str(change))

elif str(finalTotalPrice).endswith("5" or "0") :
    print("Final Total = $" + str(finalTotalPrice))
    print()

    cashPayment = float( input("How much cash will you pay with? $"))
    change = (cashPayment - finalTotalPrice)
    change = round(change, 3)
    print("Your change is $" + str(change))


Upvotes: 1

Views: 179

Answers (2)

wizzwizz4
wizzwizz4

Reputation: 6426

As pointed out by tripleee, you're actually trying to do logically the wrong thing.

roundedDown = round(finalTotalPrice, 1)

This suggests that finalTotalPrice is a float. Never use floats for money. It'll mostly work for small values, but one day things will stop adding up. Instead, use an integer representing the amount of the smallest denomination you have – here, it looks like your dollar is divided into mils (you used round(..., 3)), so a dollar should be represented as 1000.

But can you still use the round function?

>>> help(round)
Help on built-in function round in module builtins:

round(number, ndigits=None)
    Round a number to a given precision in decimal digits.
    
    The return value is an integer if ndigits is omitted or None.  Otherwise
    the return value has the same type as the number.  ndigits may be negative.

Yes, you can; just put in -1 to round to the nearest 10, -2 to round to the nearest 100, etc..

str(finalTotalPrice).endswith("1" or "2")

Ignoring the bug for a moment, this is a bad solution. If you're using floats, it will usually give a completely wrong answer, and if you're using ints it's inefficient. Once you're using ints, you can fix this; finalTotalPrice % 10 will get the last digit, finalTotalPrice % 100 will get the last two digits, etc.. Then you can do:

if finalTotalPrice % 10 in (1, 2):
if finalTotalPrice % 10 > 7:
if finalTotalPrice % 5 == 0:

as necessary.


Additionally, your cash payment code is identical in each if branch, so it should be written after them, not in each branch. And, while we're at it, let's make your variable names conform to PEP 8, the Python Style Guide, and improve input handling:

import re

MONEY_REGEX = re.compile(r"[$]?(\d*)(?:\.(\d+))?")
def input_dollars(prompt="", places=3):
    """Input a dollar amount, returned as an integer.
    
    The prompt string, if given, is passed to the input function.
    
    The places argument determines how many decimal places are allowed.
    The return value is shifted by that many decimal places,
    so that it is an integer.

    >>> input_dollars("prompt? ", places=2)
    prompt? a
    invalid input
    prompt? 
    empty input
    prompt? 0.
    invalid input
    prompt? $32
    3200
    >>> input_dollars("prompt? ", places=2)
    prompt? 32.450
    too many decimal places
    prompt? 32.4
    3240
    >>> input_dollars("prompt? ", places=2)
    prompt? .6
    60
    >>> input_dollars("prompt? ", places=4)
    prompt? $.472
    4720
    """

    fix = 10 ** places
    while True:
        text = input(prompt)
        match = MONEY_REGEX.fullmatch(text)
        if match is None:
            print("invalid input")
            continue
        integer, fractional = match.groups()
        if fractional is None:
            if len(integer) == 0:
                print("empty input")
                continue  
            return int(integer) * fix
        if len(fractional) > places:
            print("too many decimal places")
            continue
        ipart = int(integer) if integer else 0
        fpart = int(fractional.ljust(places, '0'))
        return ipart * fix + fpart

def format_dollars(dollars, places=3):
    fix = 10 ** places
    return "${}.{:0>{}}".format(dollars // fix, dollars % fix, places)

def print_final_total(final_total, places=3):
    print("Final Total =", format_dollars(final_total, places))
    print()

final_total_price = input_dollars("What's the final total price? ")

if final_total_price % 10 in (1, 2, 8, 9):
    print_final_total(round(final_total_price, -2))
elif final_total_price % 5 == 0:
    print_final_total(final_total_price)

cash_payment = input_dollars("How much cash will you pay with? $")
change = cash_payment - final_total_price
print("Your change is", format_dollars(change))

This code probably doesn't do what you want it to. But, then, neither does your original.

Upvotes: 1

wizzwizz4
wizzwizz4

Reputation: 6426

Python is not natural language. and and or do not behave the way you are used to. Let's look at the documentation:

>>> help(str.endswith)
Help on method_descriptor:

endswith(...)
    S.endswith(suffix[, start[, end]]) -> bool
    
    Return True if S ends with the specified suffix, False otherwise.
    With optional start, test S beginning at that position.
    With optional end, stop comparing S at that position.
    suffix can also be a tuple of strings to try.

Your if statements should look like this:

if str(finalTotalPrice).endswith(("1", "2")):

Upvotes: 3

Related Questions