# Python beginner's body fat calculator

I'm very very fresh to programming. This is one of my first experiments with Python and I'm wondering in what ways I could have made this program less clunky. Specifically, is there a way that I could have used classes instead of defining my x, y, and z variables globally?

def getx():
try:
global x
return x
except ValueError:
print("Use a number, silly!")
getx()
def gety():
try:
global y
y = float(raw_input("what is your current body fat percentage? (>1): "))
return y
except ValueError:
print("Use a number, silly!")
gety()
def getz():
try:
global z
z = float(raw_input("What is your desired body fat percentage? (>1): "))
return z
except ValueError:
print("Use a number, silly!")
getz()
def output():
getx()
gety()
getz()
A = (x*(y/100-z/100))/(1-z/100)
B = x - A
print("Your necessary weight loss is %.1f pounds, and \
your final weight will be %.1f pounds" % (A,B))
more()
def more():
again = raw_input("Calculate again? ")
if again.lower() == "yes" or \
again.lower() == "y" or \
again.lower() == "sure" or \
again.lower() == "ok" or \
again.lower() == "" or \
again.lower() == "okay":
output()
elif again.lower() == "no" or \
again.lower() == "n" or \
again.lower() == "nah" or \
again.lower() == "nope":
end()
else:
more()
def end():
print("Ok, see ya later!")
output()

• This belongs on codereview, but I don't see how classes would help. You might want to get rid of the global variables, however and just pass things in via function arguments. Commented Aug 9, 2012 at 19:50
• since all three "get" functions are almost identical, you can reduce them to one that takes a parameter for the input prompt. For the if statements, you can say ... if again.lower() in ["yes", "y", ...]:
– jcfollower
Commented Aug 9, 2012 at 19:56

all of your functions seem to do the same thing with a different message, so why not condense them and take the message as a parameter?

def get_num(msg):
num = None
while num is None:
try:
num = float(raw_input(msg))
except ValueError:
print("Use a number, silly!")

return num


and then

def output():
y = get_num('what is your current body fat percentage? (>1): ')
z = get_num('What is your desired body fat percentage? (>1): ')
A = (x*(y/100-z/100))/(1-z/100)
B = x - A
print("Your necessary weight loss is %.1f pounds, and \
your final weight will be %.1f pounds" % (A,B))
more()


in your more function you can condense your ifs with the in operator

def more():
again = raw_input("Calculate again? ")

if again.lower() in ["yes", "y", "sure" , "ok", "", "okay"]:
output()
elif again.lower() in ["no", "n", "nah", "nope"]:
end()
else:
more()


Do you have objects, "things" that have state and behaviour? I don't see any. So use functions as you do (perhaps improve the code format).

In getx, gety and getz there's no need to use global and return. Also, it would be better to use iteration rather than recursion, like this:

def getx():
while True:
try:
except ValueError:
print("Use a number, silly!")


You might also want to use better function and variable names.

Yes, you definitely can. You can try something in the lines of:

class PersonData(object):
def __init__(self):
pass

def getx(self):
try:
except ValueError:
print("Use a number, silly!")
self.getx()


.. and so on.

And then in your main program:

if __name__ == "__main__":
person = PersonData()
person.getx()
person.gety()

...

A = (person.x * (person.y / 100 - person.z / 100))/(1 - person.z / 100)


If you get my drift. This is generally if you want to use classes. Otherwise see other answers :)

The code below condenses your script (fewer lines and redundancy when you remove the triple quote explanations). Honestly, using classes would complicate your function, so unless you need the classes for something else, just do all in the function. Also, typically it is better to not call globals unless you need to. For your purpose, unless there is more to the script, use local variables.

Your script suffers a lot from redundancy. It works fine, but there are easier ways to do it. Instead of reusing again.lower() ==, put all the desired responses into a list. Using 'is...in' checks if your variable is in you list (or string, etc). For example, if var in ["y", "yes"]:

You can further reduce your redundancy by making a function to do your try/except statements, but I used while statements to show another way to do it. You can then use continue (which resets back to the beginning) if there is an exception and use break to exit the loop if you try statement succeeds.

Note: I tested this on Pythonista for iOS using 2.7. I added in the triple quotes as explanations after confirming the script worked and using as is may throw an indentation error. Removing the triple quote explanations should run it properly.

""" Unless you are reusing variables, but it all in
the same function"""

def output():
""" Put your inputs in a while statement. Continue on
exception and break otherwise"""
while True:
try:
except ValueError:
print("Use a number, silly!")
continue
break
while True:
try:
y = float(raw_input("what is your current body fat percentage? (>1): "))
except ValueError:
print("Use a number, silly!")
continue
break
while True:
try:
z = float(raw_input("What is your desired body fat percentage? (>1): "))
except ValueError:
print("Use a number, silly!")
continue
break
A = (x*(y/100-z/100))/(1-z/100)
B = x - A
print("Your necessary weight loss is %.1f pounds, and \
your final weight will be %.1f pounds" % (A,B))

""" Like before put input into while statement"""

while True:
again = raw_input("Calculate again? ")

""" Condense your input options into an if in statement"""
if again.lower() in ["yes", "y", "sure", "ok", "", "okay"]:
output()

""" Unless No response is important, just use else
catchall"""
else:
print("Ok, see ya later!")
break
output()

• How and why does this condense the original script? Can you explain why you did the things you did? Commented Apr 19, 2017 at 17:36
• Also, please verify your indentation. Commented Apr 19, 2017 at 18:13
• @StephenRauch It's fewer lines and less redundancy. For example, instead of rewriting each "again.lower() ==" line, it puts all desirable responses into a list and checks if the response is in the list. Unless 'No' is very important, any answer that isn't yes can be 'No' - otherwise, put in a list and check. I explained my changes inside of the script. The while statements removes the need for separate functions. Continue runs the previous try and break ends the statement. This can be further condensed, but I wanted to show the while/continue/break combination with try/except. Commented Apr 19, 2017 at 18:51
• @200_success I wrote this on Pythonista on my phone, so indentation may be off compared to desktop version. Also, I added the notes explaining changes after I verified the function worked and the notes are throwing me the indentation errors. Remove the triple quote notes and it all runs fine. Commented Apr 19, 2017 at 18:53
• Thanks for the feedback, but you should edit this explanation into the answer so that it is helpful for future readers. Thanks. Commented Apr 19, 2017 at 18:56