Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can I simplify repetitive if-elif statements in my grading system function?

The goal is to build a program to convert scores from a '0 to 1' system to an 'F to A' system:

  • If score >= 0.9 would print 'A'
  • If score >= 0.8 would print 'B'
  • 0.7, C
  • 0.6, D
  • And any value below that point, print F

This is the way to build it and it works on the program, but it's somewhat repetitive:

if scr >= 0.9:
    print('A')
elif scr >= 0.8:
    print('B')
elif scr >= 0.7:
    print('C')
elif scr >= 0.6:
    print('D')
else:
    print('F')

I would like to know if there is a way to build a function so that the compound statements wouldn't be as repetitive.

I'm a total beginner, but would something in the lines of :

def convertgrade(scr, numgrd, ltrgrd):
    if scr >= numgrd:
        return ltrgrd
    if scr < numgrd:
        return ltrgrd

be possible?

The intention here is that later we can call it by only passing the scr, numbergrade and letter grade as arguments:

convertgrade(scr, 0.9, 'A')
convertgrade(scr, 0.8, 'B')
convertgrade(scr, 0.7, 'C')
convertgrade(scr, 0.6, 'D')
convertgrade(scr, 0.6, 'F')

If it would be possible to pass fewer arguments, it would be even better.

like image 471
Matheus Bezerra Soares Avatar asked Apr 04 '20 15:04

Matheus Bezerra Soares


3 Answers

You can use the bisect module to do a numeric table lookup:

from bisect import bisect 

def grade(score, breakpoints=(60, 70, 80, 90), grades='FDCBA'):
     i = bisect(breakpoints, score)
     return grades[i]

>>> [grade(score) for score in [33, 99, 77, 70, 89, 90, 100]]
['F', 'A', 'C', 'C', 'B', 'A', 'A']
like image 186
dawg Avatar answered Oct 11 '22 04:10

dawg


You can do something along these lines:

# if used repeatedly, it's better to declare outside of function and reuse
# grades = list(zip('ABCD', (.9, .8, .7, .6)))

def grade(score):
    grades = zip('ABCD', (.9, .8, .7, .6))
    return next((grade for grade, limit in grades if score >= limit), 'F')

>>> grade(1)
'A'
>>> grade(0.85)
'B'
>>> grade(0.55)
'F'

This uses next with a default argument on a generator over the score-grade pairs created by zip. It is virtually the exact equivalent of your loop approach.

like image 12
user2390182 Avatar answered Oct 11 '22 04:10

user2390182


You could assign each grade a threshold value:

grades = {"A": 0.9, "B": 0.8, "C": 0.7, "D": 0.6, "E": 0.5}

def convert_grade(scr):
    for ltrgrd, numgrd in grades.items():
        if scr >= numgrd:
            return ltrgrd
    return "F"
like image 6
Nico Avatar answered Oct 11 '22 05:10

Nico