Alternatives of repeatedly using elif statements

Viewed 87

Im trying to return the 'students' grades according to what they got on python. I used a bunch of elif statements but i dont feel like this is efficient. I was wondering if there are any other ways i can present this as? possibly smaller. Ive attempted using for loops and dictionaries, but im not getting it, maybe cuz i dont understand dictionaries well loll

def grade_select(grade):
    if grade <  40: return "D-"
    elif grade <= 40 and grade < 45: return "D"
    elif grade <= 45 and grade < 50: return "D+"
    elif grade <= 50 and grade < 55: return "C-"
    elif grade <= 55 and grade < 60: return "C"
    elif grade <= 60 and grade < 65: return "C+"
    elif grade <= 65 and grade < 70: return "B-"
    elif grade <= 70 and grade < 75: return "B"
    elif grade <= 75 and grade < 80: return "B+"
    elif grade <= 80 and grade < 85: return "A-"
    elif grade <= 85 and grade < 90: return "A" 
    elif grade >= 90: return "A+"
5 Answers

since you return when a condition is matched, you do not need to use elif just if.

moreover, the previous condition eliminates the need of checking if the value is "between".

def grade_select(grade):
    if grade < 40: return "D-"
    if grade < 45: return "D"
    if grade < 50: return "D+"
    if grade < 55: return "C-"
    if grade < 60: return "C"
    if grade < 65: return "C+"
    if grade < 70: return "B-"
    if grade < 75: return "B"
    if grade < 80: return "B+"
    if grade < 85: return "A-"
    if grade < 90: return "A" 
    return "A+"

an alternative would be to declare a dictionary and calculate where the grade fits.

grades = { 40: "D-", 45: "D", 50: "D+", 55: "C-", 60: "C", 65: "C+", 70: "B-", 75: "B", 80: "B+", 85: "A-", 90: "A", 100: "A+" }

def grade_select(grade):
    grade = (grade // 5 * 5) + 5

    if grade < 40:
        grade = 40
    elif grade > 90:
        grade = 100

    return grades[grade]

You can use a dictionary to store the range and it's according grade value:

def grade_select(grade):
    dct = {
        range(0, 40): "D-",
        range(40, 45): "D",
        range(45, 50): "D+",
        ...
        range(90, 100 + 1): "A+"
    }
    dct = {g: m for r, m in dct.items() for g in r}
    return dct[grade]

Well, your condition is kinda weird, because

elif grade <= 40 and grade < 50

has no sense (if grade <= 40 then grade < 50 of course)

But I think you wanna write something like this:

if 40 <= grade < 50:
    pass

You can use syntax above, it could make it a little bit better. Also, you can check this article

Here's the fastest version that calculates an index based on the grade value. This method only works if your grade ranges are all five wide.

_lettergrades = ['D-', 'D', 'D+', 'C-', 'C', 'C+', 'B-', 'B', 'B+', 'A-', 'A', 'A+']

def grade_select(grade):  
    return _lettergrades[(max(min(grade, 90), 35) - 35) // 5]

If your ranges weren't consistently five wide you could use bisect.bisect_right to get an index for the letter grade. The search uses bisection, which should be faster than a linear search. (The lists _thresholds and _lettergrades are global to avoid recreating them for every call to grade_select().)

from bisect import bisect_right
    
_thresholds = [40, 45, 50, 55, 60, 65, 70, 75, 80, 85, 90]
_lettergrades = ['D-', 'D', 'D+', 'C-', 'C', 'C+', 'B-', 'B', 'B+', 'A-', 'A', 'A+']

def grade_select(grade):
    return _lettergrades[bisect_right(_thresholds, grade)]

Note: I doubt speed matters as much as clarity here. For clarity I like Paulo Pereira's answer best.

I guess using dictionary is the best way

def grade_select(grade):
    d = {
        'D-':range(0,40),
        'D':range(45,50),
        'D+':range(50,55),
        'C-':range(55,60) #, etc.
    }

    return [k for k,v in d.items() if grade in v][0]
Related