Introduction
After reflecting and reading other answers, I found that Adam Smith is right: we should get rid of the class. For this problem I think that it's better to structure the program as a group of functions. This is my new answer.
Style
Variable names should be lowercase, so your variable BMI
should be renamed bmi
.
Modularity
According to Wikipedia, "In software design, modularity refers to a logical partitioning of the "software design" that allows complex software to be manageable for the purpose of implementation and maintenance. The logic of partitioning may be based on related functions, implementation considerations, data links, or other criteria." So you should divide your code into smaller units, for this purpose we will divide your code into a group of functions.
This is better, because it's a bad practice, IMHO, to write all your code as a single whole without dividing it into units. This makes your code reusable, readable, and maintainable.
Documentation
It's a good practice, IMHO, to document your code, this makes it more readable and easier to use. For this purpose we will use docstrings to document the functions that we'll define. Docstings are indented and contained into triple quotes, they describe what the function does, its arguments, the type of its arguments, its return value and the type of the return value (when used with functions). Docstrings are the right way to document functions, not comments, because if you need help about using the function, you can use help(name here)
to display the docstring and get help, which you can't do with comments.
Dividing the Code into Functions
To achieve modularity, we'll divide the code into functions. We'll define:
a boolen-valued function (a function that returns only True
or False
) that asks whether the user wants to start. We will rename to_start
to answer
. Using upper
method, we'll make the input uppercase, so we can compare it to 'Y' and 'N' easily if the user entered 'y' or 'n' instead.
a function that handles both weight and height.
a function that takes calculates the BMI and returns it.
a function that returns the weight status.
a function called main
that contains all the code that will be executed. We'll use it as the body of if __name__ == '__main__'
by calling it.
Return Value
Instead of making our new functions print the output, we'll make them return it. You can read about return values here (this is a link to chapter in Think Python 2e, I recommend that you read the whole book). Just printing the output gets the job done but makes functions less reusable. Suppose that you want to store the BMIs associated with users in a textfile, what will you do? Sure you can make other functions to get this done, but if you make your functions return the result you can reuse many cases.
Code after Refactoring
def body_mass_index(weight, height):
"""Returns the BMI based on weight and height.
weight: float or int
height: float or int
Returns: float or int
"""
return round(weight / height ** 2 , 2)
def weight_category(bmi):
"""Returns the weight category of the user based on their BMI.
Returns: str
"""
if bmi < 18.5:
return 'underweight'
elif 18.5 <= bmi <= 24.99:
return 'healthy weight'
elif 25.0 <= bmi <= 29.99:
return 'overweight'
elif bmi > 30:
return 'obese'
def wants_to_start():
"""Asks whether the user wants to start.
Returns: bool
"""
while True:
answer = input("Y to start N to exit: ").upper()
if answer == 'Y':
return True
elif answer == 'N':
return False
print('Please try again.\n')
def get_user_data():
"""Asks for the user's weight and height.
Rerurns: tuple of 2 floats or integers
"""
while True:
try:
weight = float(input('Enter your weight in kilograms: '))
height = float(input('Enter your height in meters: '))
if 0 < weight and 0 < height:
return weight, height
else:
raise ValueError()
except ValueError:
print('Invalid input for weight or height')
continue
def main():
if wants_to_start():
weight, height = get_user_data()
bmi = body_mass_index(weight, height)
category = weight_category(bmi)
print('Your BMI is: ', bmi)
print('Your are ', category, '.')
else:
quit()
if __name__ == '__main__':
main()
Notes
The function get_user_data
is adopted from Lukasz Salitra's function _get_user_info
in his answer to the OP's previous question. You can find the question here.
I'm no expert on BMI, so I've no idea if the program does what it's supposed to do properly. I focused on making the code more reusable, readable, and well-structured, according to what I know as a beginner.