Simple Python code error

I have some simple python code (version 2.7.3) that has an output that I cannot understand. The code asks the user for a rating (and will continue to do so if the input is anything other than a number between 0 and 1), determines the letter class, and then exits. The code looks like this:

def calc_grade():
    try:
        score = float(raw_input("Enter a score: "))
        if score > 1.0:
            print "Error: Score cannot be greater than 1."
            calc_grade()
    except:
        print "Error: Score must be a numeric value from 0 to 1."
        calc_grade()

    print "\nthe score is: %s" % (score)
    if score >= 0.9:
        print "A"
    elif score >= 0.8:
        print "B"
    elif score >= 0.7:
        print "C"
    elif score >= 0.6:
        print "D"
    else:
        print "F"
    return 0
calc_grade()

      

If I run this script try the inputs: 1.5, h, 0.8, then I get the following output:

Enter a score: 1.5
Error: Score cannot be greater than 1.
Enter a score: h
Error: Score must be a numeric value from 0 to 1.
Enter a score: 0.8

the score is: 0.8
B
Error: Score must be a numeric value from 0 to 1.
Enter a score: 0.7

the score is: 0.7
C

the score is: 1.5
A

      

As you can see, after entering a valid value (0.8), the script outputs the correct score (B), but then the script does not end as I expect. Instead, it prints an error message for a non-numeric value and then prompts the user to re-enter the account. If I enter another valid score (in this case 0.7), then the script prints out the correct grade (C) and then prints out the first invalid input (1.5) along with its class (A).

I cannot, for the life of me, figure out what is causing this, "functionality". Any suggestions?

+3


source to share


5 answers


If any error occurs, you are calling calc_grade

recursively again, so if you enter invalid input, you will have multiple calls. Instead, you should be able to handle erroneous errors iteratively:



def calc_grade():
    score = None
    while score is None:     
        try:
            score = float(raw_input("Enter a score: "))
            if score > 1.0:
                print "Error: Score cannot be greater than 1."
                score = None
        except:
            print "Error: Score must be a numeric value from 0 to 1."

    # If we reached here, score is valid,
    # continue with the rest of the code

      

+7


source


Here's what happened:



When you passed the value "h" to your function, casting "h" before float

failed, resulting in a ValueError. Your operator except

caught the error and then called again calcGrade()

. This new call received an argument .8 and returned fine. When the call to .8 returned, it returned control back to the call that received "h" as an argument. Then the challenge run the following command: print "\nthe score is: %s" % (score)

. Since the lead to swimming failed, a score was never assigned. So this call calcGrade()

throws UnboundLocalError

, which is then captured by its caller, which is the instance calcGrade()

that was passed the value 1.5 (as @ZackTanner pointed out). Recall that the call to "h" was called from within the block try

.

+1


source


Recusion bites you because after the recursion you have additional code. @ Mureink's answer is the correct way to handle this. The other is to execute the data entry function that it does:

def get_input():
    try:
        score = float(raw_input("Enter a score: "))
        if score > 1.0:
            print "Error: Score cannot be greater than 1."
            return get_input()
    except ValueError:
        print "Error: Score must be a numeric value from 0 to 1."
        return get_input()
    return score

def calc_grade():
    score = get_input()
    print "\nthe score is: %s" % (score)
    if score >= 0.9:
        print "A"
    elif score >= 0.8:
        print "B"
    elif score >= 0.7:
        print "C"
    elif score >= 0.6:
        print "D"
    else:
        print "F"
    return 0
calc_grade()

      

This method returns the entered value when the user enters a valid value. When they don't, it returns the call value get_input()

. this sums up all the recursions ready to return whatever is returned to them. When the user finally enters the correct answer, the entire recursion stack collapses, returning the valid answer as entered by the user.

The call get_input()

inside calc_grade

will continue until the user enters a valid answer. At this time, it get_input

will stop processing and return this valid user input calc_grade

to calc_grade

for processing.

0


source


You forget that recursion does not terminate the previous function call. So when you call calc_grade()

on error, you go back to the original calc_grade()

in print "the score is:"

and therefore print it multiple times.

Now, to fix your code, I'll just add some returs:

def calc_grade():
    try:
        score = float(raw_input("Enter a score: "))
        if score > 1.0:
            print "Error: Score cannot be greater than 1."
            calc_grade()
            return
    except:
        print "Error: Score must be a numeric value from 0 to 1."
        calc_grade()
        return    
    print "\nthe score is: %s" % (score)
    if score >= 0.9:
        print "A"
    elif score >= 0.8:
        print "B"
    elif score >= 0.7:
        print "C"
    elif score >= 0.6:
        print "D"
    else:
        print "F"
calc_grade()

      

Python doesn't require you to write anything after returning, you can use it to simply exit the function.

Also I recommend using it str.format

as a formatting option %

.

This is how I would do it without changing the code too much:

def calc_grade():
    try:
        score = float(raw_input("Enter a score: "))
        if score > 1.0:
            raise TypeError
    except ValueError:
        print "Error: Score must be a numeric value from 0 to 1."
    except TypeError:
        print "Error: Score cannot be greater than 1."
    except:
        print "Error: Unexpected error, try again."
    else:
        if score >= 0.9:
            score = "A"
        elif score >= 0.8:
            score = "B"
        elif score >= 0.7:
            score = "C"
        elif score >= 0.6:
            score = "D"
        else:
            score = "F"
        print "the score is: {}".format(score)
        return
    calc_grade()

calc_grade()

      

0


source


At first , you cannot call inside calc_grade()

. This will lead to many errors. You can only call it once, but you can print it as many times as you like. Second , try

and except

perhaps not the best way to do this. Try to make class

and make functions from there. try

and except

will run every time your code is run. Third , if you run a number between any of these numbers, it will be print

all letters up to the maximum. I have a code similar to yours, it calculates 3 people counts. Here is a website to help you understand errors and exceptions better. https://docs.python.org/2/tutorial/errors.html

Here is my code
    lloyd = {
        "name": "Lloyd",
        "homework": [90.0, 97.0, 75.0, 92.0],
        "quizzes": [88.0, 40.0, 94.0],
        "tests": [75.0, 90.0]
    }
    alice = {
        "name": "Alice",
        "homework": [100.0, 92.0, 98.0, 100.0],
        "quizzes": [82.0, 83.0, 91.0],
        "tests": [89.0, 97.0]
    }
    tyler = {
        "name": "Tyler",
        "homework": [0.0, 87.0, 75.0, 22.0],
        "quizzes": [0.0, 75.0, 78.0],
        "tests": [100.0, 100.0]
    }

# Add your function below!
def average(numbers):
    total = sum(numbers)
    total = float(total)
    return total/len(numbers)
def get_average(student):
    homework_ave=average(student["homework"])
    quizzes_ave=average(student["quizzes"])
    tests_ave=average(student["tests"])
    return 0.1 * average(student["homework"]) + 0.3 *         average(student["quizzes"]) + 0.6 * average(student["tests"])
def get_letter_grade(score):
    if 90 <= score:
        return "A"
    elif 80 <= score:
        return "B"
    elif 70 <= score:
        return "C"
    elif 60 <= score:
        return "D"
    else:
        return "F"
print get_letter_grade(get_average(lloyd))
def get_class_average(students):
    results = []
    for student in students:
        results.append(get_average(student))
    return average(results)
students = [lloyd, alice, tyler]
print get_class_average(students)
print get_letter_grade(get_class_average(students))

      

0


source







All Articles