-3

I'm taking a tutorial in udemy to teach myself Python and it is making me love Javascript every day more. Anyhow, I can't seem to figure out how to work this indentation right so that the program runs inside a while loop that evaluates a zero string to exit BUT all input and calculations must be inside functions.

I've got this far, but the weight, height, and bmi variables are throwing undefined errors. Problem is, as far as I know, didn't I define them already in their functions?

NameError: name 'weight' is not defined

So, I thought, maybe it's because I need to indent the function calls to give the variable declarations a higher scope and what do you know! the evil red squiggly underline went away. So I ran the program with no errors and but nothing ran inside the while loop. it ignored everything! ugh....

Any help please. God, I love squiggly brackets and semicolons.

def BMICalculator():
    student_name = input("Enter the student's name or 0 to exit:")
    while student_name != "0":
        def getWeight():
            weight = float(input("Enter " + student_name + "'s weight in pounds: "))
            return weight

        def getHeight():
            height = float(input("Enter " + student_name + "'s height in pounds: "))
            return height

        def calcBMI(weight, height):
            bmi = (weight * 703) / (height * height)
            return bmi

        getWeight()
        getHeight()
        calcBMI(weight, height)
        print("\n" + student_name + "'s BMI profile:")
        print("---------------")
        print("Height:", height, "\nWeight:", weight)

        def showBMI(bmi):
            print("BMI Index:", "{:.2f}".format(bmi))

        showBMI(bmi)

        student_name = input("Enter next student's name or 0 to exit:")


    print("\n\nExiting Program!")


BMICalculator()
LOTUSMS
  • 10,317
  • 15
  • 71
  • 140
  • 2
    What do you think `return weight` does with respect to `getWeight()`? How do you assign a value to the return value in JavaScript? – OneCricketeer Jan 21 '17 at 00:08
  • 5
    And suggestion: You really should not define methods within loops – OneCricketeer Jan 21 '17 at 00:09
  • 1
    Just like in JavaScript or any other good language, `getWeight()` should be `weight = getWeight()`, etc. – DYZ Jan 21 '17 at 00:10
  • I'm following their instruction. And given this is uncharted territory to me, I didn't want to assume anything. But your comments are giving me good tips to keep working on it. Thanks – LOTUSMS Jan 21 '17 at 00:11
  • By the way, the down votes are unnecessary. I'm going to delete this anyway but have at it. Sorry my question offended your brilliance in python programming. lol – LOTUSMS Jan 21 '17 at 00:13

3 Answers3

4

didn't I define them already in their functions

Yes, and that's the problem. They are locally scoped to those functions. You need assignment... weight = getWeight(), for example


No need to go overboard with the function definitions, though

def calcBMI(weight, height):
    bmi = (weight * 703) / (height * height)
    return bmi

def showBMI(bmi):
    print("BMI Index:", "{:.2f}".format(bmi))

student_name = input("Enter the student's name or 0 to exit:")
while student_name != "0":
    weight = float(input("Enter " + student_name + "'s weight in pounds: "))
    height = float(input("Enter " + student_name + "'s height in pounds: "))

    bmi = calcBMI(weight, height)

    # etc...

    showBMI(bmi)
    student_name = input("Enter next student's name or 0 to exit:")

print("\n\nExiting Program!")
OneCricketeer
  • 179,855
  • 19
  • 132
  • 245
  • This is great and it makes sense to me. I'll still have to collect the names in a function Per the instructions, or it won't allow me to see the next lecture. This makes sense in a realistic use, but for academic purposes, they like to entangle things a bit. But I should be able to collect the rest by collecting getWeight() in the weight variable as someone suggested above. I do like your answer. – LOTUSMS Jan 21 '17 at 00:18
  • Got it! it works perfect now. I'm thinking I'm gonna beat this down a little more before I move further to arrays. I don't suppose arrays are too difficult to deal with. Thanks again – LOTUSMS Jan 21 '17 at 00:26
  • They are called lists in Python, and IMO, they aren't – OneCricketeer Jan 21 '17 at 02:13
3

You are getting the weight, height and bmi, but you are just throwing away the result. These variables are local to the inner function scopes, so they are invisible for your outer function.

Doing:

weigth = getWeight()
height = getHeight()
bmi    = calcBMI(weight, height)

Fixes the problem.

Also, a note on squiggly brackets and semicolons; In Python an indented block with the same indentation level is equivalent to a pair of squiggly brackets in other languages. While line breaks translates to semicolons.

    a = 1
    b = 2
    if True:
        c = 3

<=>

{
    a = 1;
    b = 2;
    if (True) {
        c = 3;
    }
}

However, there are a few (convenient) exceptions. Like when a line is ended without a closing parenthesis, no "virtual semicolon" is inserted. Also, an inner Python scope does not hide its local variables to an outer scope unless the scoping is due to a function.

nijoakim
  • 930
  • 10
  • 25
1

You are accessing a variable outside the scope. For instance the weight variable is defined in the scope of the getWeight() function but you are trying to access it from the BMICalculator() scope.

Quick solution

You should properly scope your variables, you have two options. Define the functions as closures or just use the return value. Below I how the second option.

def BMICalculator():
    student_name = input("Enter the student's name or 0 to exit:")
    while student_name != "0":
        def getWeight():
            weight = float(input("Enter " + student_name + "'s weight in pounds: "))
            return weight

        def getHeight():
            height = float(input("Enter " + student_name + "'s height in pounds: "))
            return height

        def calcBMI(weight, height):
            bmi = (weight * 703) / (height * height)
            return bmi

        # here you have to define the weight, height and bmi
        # variables in this scope.
        weight = getWeight()
        height = getHeight()
        bmi = calcBMI(weight, height)
        print("\n" + student_name + "'s BMI profile:")
        print("---------------")
        print("Height:", height, "\nWeight:", weight)

        def showBMI(bmi):
            print("BMI Index:", "{:.2f}".format(bmi))

        showBMI(bmi)

        student_name = input("Enter next student's name or 0 to exit:")


    print("\n\nExiting Program!")

Comments

To clarify, you should try moving the functions outside the loop (putting them at the same level as BMICalculator(). This will probably clarify the scoping problem to you.

def getWeight():
    weight = float(input("Enter " + student_name + 
                         "'s weight in pounds: "))
    return weight

def getHeight():
    height = float(input("Enter " + student_name + 
                         "'s height in pounds: "))
    return height

def calcBMI(weight, height):
    bmi = (weight * 703) / (height * height)
    return bmi

def BMICalculator():
    student_name = input("Enter the student's name or 0 to exit:")
    while student_name != "0":
        # Now it is more evident that the weight, height and bmi
        # variables in the above functions are only defined in the
        # scope of the respective function, the BMICalculator scope
        # is different and the names "weight", "height" and "bmi" are
        # not bound to anything, hence the NameError.
        weight = getWeight()
        height = getHeight()
        bmi = calcBMI(weight, height)
        print("\n" + student_name + "'s BMI profile:")
        print("---------------")
        print("Height:", height, "\nWeight:", weight)

        def showBMI(bmi):
            print("BMI Index:", "{:.2f}".format(bmi))

        showBMI(bmi)

        student_name = input("Enter next student's name or 0 to exit:")

Scoping in python has some simple yet important rules, I suggest you have a look at this answer for a quick overview of the scope resolution.

As an aside, the problem you are having is not python-related, any decent (or at least those I know) programming language (even javascript) would treat those variables as out of scope.

Hope this helps.

qwattash
  • 855
  • 7
  • 14
  • I agree, JS would had treat them out of scope. I just have a hard time figuring out the scope problem in python. In JS I can close and function and everything in it has local scope anything out side it a larger scope. Simple. Python uses indentation which to me it still means code cleanliness. ::shrugs::. Thanks anyway. I like your answer – LOTUSMS Jan 21 '17 at 00:31
  • Why did you keep the definition of `showBMI` in the loop? FWIW, defining a function inside a loop is rather inefficient since it discards the old function object & re-builds it on every loop iteration. – PM 2Ring Jan 21 '17 at 08:22
  • That example was only meant to clarify the scoping problem with the functions the OP was talking about. I did not want to change parts that are not relevant to that, but yes, you are absolutely right, that function definition is inefficient and it should not be there. Also I see no point in using inner functions like that anyway. – qwattash Jan 23 '17 at 10:04