I am trying to build a basic (beginner) project for a BMI calculator that can accept both metric and imperial measurements, I am trying to include raise
statements to display an error if the entered unit of measurement isn't valid like if the user were to enter both imperial and metric or neither. I believe I have used the statements correctly but even if it the unit is entered correctly it still displays the error.
import sys
unit = input("To begin, please input your units of measurement (Imperial or Metric) ")
#error if user doesn't enter imperial or metric
if unit.lower().__contains__("metric" or "imperial") == False:
sys.tracebacklimit = 0
raise Exception("An error has occurred!\n\nPlease check if your unit of
measurement is valid/spelled correctly")
exit(1)
#error if user enters both imperial and metric
if unit.lower().__contains__("metric" and "imperial"):
sys.tracebacklimit = 0
raise Exception("An error has occurred!\n\nPlease check if your unit of
measurement is valid/singular")
exit(1)
I've tried removing them and it works but I want to keep them. I've tried changing the further if statements to elif, to no avail. I have also tried messing around with the statements like changing if unit.lower().__contains__("metric" or "imperial") == False:
to if not unit.lower().__contains__("metric" or "imperial")
etc. and again had no success with it. Is there anything that can be done?
Edit: This is only happening when "imperial" is entered
CodePudding user response:
"metric" or "imperial"
evaluates to "metric"
, as you can see by pasting it into the REPL:
>>> "metric" or "imperial"
'metric'
so unit.lower().__contains__("metric" or "imperial")
is the same as saying "metric" in unit.lower()
, which will of course be false (and produce your error) if the unit is "imperial":
>>> unit = "imperial"
>>> "metric" in unit
False
Instead you could use an or
in between the tests:
>>> "metric" in unit or "imperial" in unit
True
or put the units into an iterable and use the any
function:
>>> any(u in unit for u in ("metric", "imperial"))
True
CodePudding user response:
You are testing for the existence of True and False in your current code. An easier way to do it is to just use the in
keyword, and splitting up the expected terms.
It is much more readable this way as well.
import sys
unit = input("To begin, please input your units of measurement (Imperial or Metric) ")
#error if user doesn't enter imperial or metric
if unit.lower() not in ["metric", "imperial"]:
sys.tracebacklimit = 0
raise Exception("An error has occurred!\n\nPlease check if your unit of
measurement is valid/spelled correctly")
sys.exit(1)
#error if user enters both imperial and metric
if "metric" in unit.lower() and "imperial" in unit.lower():
sys.tracebacklimit = 0
raise Exception("An error has occurred!\n\nPlease check if your unit of
measurement is valid/singular")
sys.exit(1)
CodePudding user response:
or
does not do what you think it does here. or
will return the first non None
or False
it finds. For example: False or 12 = None or 12 = 12 or None = 12
. Also you should try to avoid directly calling dunder (double underscored) methods when you can. __contains__
is used for the in
function. A better implementiation would therefore be:
import sys
unit = input("To begin, please input your units of measurement (Imperial or Metric) ").lower()
#error if user doesn't enter imperial or metric
if "metric" not in unit and "imperial" not in unit:
sys.tracebacklimit = 0
raise Exception("An error has occurred!\n\nPlease check if your unit of measurement is valid/spelled correctly")
exit(1)
#error if user enters both imperial and metric
if "metric" in unit and "imperial" in unit:
sys.tracebacklimit = 0
raise Exception("An error has occurred!\n\nPlease check if your unit of measurement is valid/singular")
exit(1)
Edit: the reason that your code only errors when "imperial" is entered is because of the or
behaviour I described. "metric" or "imperial"
is evaluated to just "metric"
so unit.lower().__contains__("metric" or "imperial") == False
is the same as unit.lower().__contains__("metric") == False
which is obviously not the behaviour you want.