Dismiss Notice
Join Physics Forums Today!
The friendliest, high quality science and math community on the planet! Everyone who loves science is here!

Python What's wrong with my if-else statements in Python 3?

  1. Sep 30, 2018 #1
    Goal:
    I'm trying to allow the user to input varying spellings for certain auto services, and still have my code work properly. My goal was to use the if-else statements to ensure that if the user misspelled a service, the code could correct the error by changing the variable assignment to the string that matched my dictionary key.
    The issue:
    The code will output: Tire rotation for any input that I enter for auto_serv. Where have I made a mistake? Any better ideas to program this? Keep in mind, I'm a first time programmer that's doing this for class, and I just learned if-else statements.
    The code:
    Code (Python):

    # dictionary assigns the cost for each service
    services = {'Oil change': 35,
                'Tire rotation': 19,
                'Car wash': 7,
                'Car wax': 12}
    # auto_serv is the user's desired car service
    auto_serv = input('Desired auto service:')
    # The following four 'if' statements are to allow the user multiple variances in spelling of desired auto service
    if auto_serv == 'Tirerotation' or 'tirerotation':
        auto_serv = 'Tire rotation'

    elif auto_serv == 'Oilchange' or 'oilchange':
        auto_serv = 'Oil change'

    elif auto_serv == 'Carwash' or 'carwash':
        auto_serv = 'Car wash'

    elif auto_serv == 'Carwax' or 'carwax':
        auto_serv = 'Car wax'

    # the if-else statements are to give a result for each service the user requests
    if auto_serv == 'Tire rotation':
        print('You entered:', auto_serv)
        print('Cost of %s: $%d' % (auto_serv, services[auto_serv]))

    elif auto_serv == 'Oil change':
        print('You entered:', auto_serv)
        print('Cost of %s: $%d' % (auto_serv, services[auto_serv]))

    # ...there are more elif statements that follow this code with the other auto services
     
     
  2. jcsd
  3. Sep 30, 2018 #2

    jedishrfu

    Staff: Mentor

    One thing you can do is convert the input to either all lowercase or all uppercase, strip the input to remove spaces at the from or back and change your if expressions accordingly. Next you can drop the first if/elif group as they just map the input to an acceptable string.
     
  4. Sep 30, 2018 #3
    Your if/elif statements need to be changed to, for example:

    if auto_serv == 'Tirerotation' or auto_serv == 'tirerotation':
     
  5. Sep 30, 2018 #4
    tldr; wot she said ^

    I'm not a Python programmer but, given the output and assuming no warnings popped up, it looks like your current code is telling the compiler to do a bit-logic operation on the two strings, which is way above your pay grade as a "first time programmer".

    As far as "could it be better?" is concerned, sure, but - more importantly - does it incorporate everything you've learned up to this point in the course, plus what you could reasonably be expected to investigate on your own.
     
  6. Sep 30, 2018 #5

    phinds

    User Avatar
    Gold Member

    To say what the others have said, but in different words,

    if auto_serv == 'Tirerotation' or 'tirerotation':

    Translates, depending on the language, to:

    1) if (auto_serv == "Tirerotation') or (if "tirerotation' evaluates to a Boolean experssion which is true) then

    2) if (auto_serv == "Tirerotation') evaluates to all 0's or all 1's and then LOGICALLY OR'ed with the binary values of 'tirerotation'

    CLEARLY #2 is not even remotely what you want and #1 is just what happens when, as Sarah pointed out, you omit the conditional on the second part.
     
  7. Sep 30, 2018 #6

    Ibix

    User Avatar
    Science Advisor

    The correction made by @Sarah Hrmbree is correct. The subsequent explanations for why your code does what it does are not, unless python 3 has completely changed handling of if and or since 2.7 (and I couldn't find any docs saying it has done). The or operator is not a bitwise operator in python; it's strictly a boolean comparison. The bitwise-or operator is | in python.

    What is happening is that your line
    Code (Python):
    if auto_serv == 'Tirerotation' or 'tirerotation':
    means if auto_serv=='Tirerotation' is true, or if 'tirerotation' is true, then do whatever. The problem is what python does when you ask if a string is true. Any non-empty string is treated as true, and only an empty string is false. Since 'tirerotation' is not an empty string the condition is always true and the first branch always executes. All of the other elif conditions also evaluate to true for the same reason, but since the first branch executed the code doesn't consider any other.

    There are many ways to improve this code. In terms of incremental improvements, I would look up python's in operator as well as considering converting to lower or upper case as @jedishrfu recommends.
     
  8. Sep 30, 2018 #7

    Ibix

    User Avatar
    Science Advisor

    ...and one more thing, @astroman707. In your second if/elif tree, have you noticed how every branch is identical? Why are you bothering making a decision if you are going to do the same thing whatever the result of the decision?

    And after you've fixed that you could look at whether you could use a similar approach to remove your first if/elif tree.
     
    Last edited: Sep 30, 2018
  9. Oct 2, 2018 #8

    harborsparrow

    User Avatar
    Gold Member

  10. Oct 10, 2018 #9
    Thank you! That was the error staring me right in the face!
     
  11. Oct 10, 2018 #10
    Thanks everyone for your inputs! Some of your suggestions are a bit too advanced for me, but still all interesting and helpful. Thanks!
     
  12. Oct 13, 2018 #11

    cronxeh

    User Avatar
    Gold Member

    [Moderator's note: off topic content deleted.]

    The right approach is to use a jaccard index to match input similarity to the list of keywords you have in your services, and return the highest matching value.
     
    Last edited by a moderator: Oct 13, 2018
  13. Oct 13, 2018 #12

    cronxeh

    User Avatar
    Gold Member

    Code (Python):
    def jaccard(a, b):
        union = list(set(a+b))
        intersection = list(set(a) - (set(a) - set(b)))
        return float(len(intersection))/len(union)

    # dictionary assigns the cost for each service
    services = {'Oil change': 35,
                'Tire rotation': 19,
                'Car wash': 7,
                'Car wax': 12}
    user_input = input('Desired auto service:')
    matching_service = sorted([(service, jaccard(user_input, service)) for service in services.keys()], key=lambda x:x[1], reverse=True)[0][0]
    print("Best matching service:", matching_service, "and cost:", services.get(matching_service))
     
    Last edited: Oct 13, 2018
  14. Oct 13, 2018 #13

    Mark44

    Staff: Mentor

    [Moderator's note: Off topic content deleted.]

    Based on the OP's statement that some of what was already said is above his current knowledge, your approach with lists and sets is probably waaaay beyond his level of expertise.
     
    Last edited by a moderator: Oct 13, 2018
  15. Oct 13, 2018 #14

    cronxeh

    User Avatar
    Gold Member

    One of his requirements was to "still have my code work properly." With his current approach it would not scale as more services are added to the dictionary, or if it is refactored to pull from a database.

    [Moderator's note: Off topic content deleted.]
     
    Last edited by a moderator: Oct 13, 2018
  16. Oct 13, 2018 #15

    PeterDonis

    Staff: Mentor

    A way to spell this that works better as more alternatives pile up is:

    if auto_serv in ('Tirerotation', 'tirerotation'):
     
  17. Oct 13, 2018 #16

    PeterDonis

    Staff: Mentor

    Some good pointers have been given for how to do this better; but you might also consider the suggestion @jedishrfu made, which, in more general contexts, goes by the name of "canonicalization": instead of trying to explicitly list all the ways users might incorrectly capitalize or misspell words, apply a function to each user input that forces it into a standard form (such as all lower case, all upper case, Capitalized First Letters Of Each Word, or whatever), and then just test for the standard form. Python's string objects have a rich set of methods for doing this; the most general is probably the "translate" method.
     
  18. Oct 13, 2018 #17

    PeterDonis

    Staff: Mentor

    This approach is probably too much for the OP given his stated experience with Python. However, it's worth pointing out that if you do this, you will end up with a matching service even if the user types in word salad. So with this approach it's a good idea to confirm with the user that your code picked the correct matching service, before doing anything else with it.
     
  19. Oct 13, 2018 #18

    PeterDonis

    Staff: Mentor

    There is no switch case statement in Python; what your link is describing is how to implement functionality that mimics what that statement does in other languages.

    Doing a dictionary lookup (including having functions as values in the dictionary) instead of multiple if/else clauses, which is what the link describes, is indeed a very useful technique in Python; I use it all the time. But it only helps with part of problem the OP is trying to solve, since it still requires an exact match in the dictionary, which means you would have to have an explicit entry for each possible misspelling of each service.
     
    Last edited: Oct 13, 2018
  20. Oct 13, 2018 #19

    PeterDonis

    Staff: Mentor

    Moderator's note: Off topic posts and content about switch statements has been deleted. Please keep discussion focused on the OP's specific problem.
     
Share this great discussion with others via Reddit, Google+, Twitter, or Facebook

Have something to add?
Draft saved Draft deleted