Як я можу спростити повторювані оператори if-elif у своїй системі оцінювання?


19

Метою є створення програми для перетворення балів із системи '0 до 1' в систему 'F до A':

  • Якби score >= 0.9надрукувати "A"
  • Якби score >= 0.8надрукувати "B"
  • 0,7, С
  • 0,6, D
  • І будь-яке значення нижче цієї точки надрукуйте F

Це спосіб її побудувати, і це працює над програмою, але це дещо повторюється:

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')

Я хотів би знати, чи є спосіб побудувати функцію, щоб складені заяви не були такими повторюваними.

Я абсолютно початківець, але хотів би щось у рядках:

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

можливо?

Наміром тут є те, що пізніше ми можемо викликати це, передаючи лише аргументи scr, numbergrade та літери як аргументи:

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')

Якби можна було передавати менше аргументів, було б ще краще.



2
Чи відповідає це на ваше запитання? Як створити систему класифікації в python?
RoadRunner

Відповіді:


29

Ви можете використовувати модуль бісектриси для пошуку цифрової таблиці:

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']

2
Я хотів би мати додатковий +1 для використання bisect, який я вважаю, що він використовується дуже рідко.
norok2

4
@ norok2 Я не думаю, що список із 4-х елементів - це місце для початку. Для таких невеликих списків лінійне сканування, ймовірно, буде швидшим. Плюс використання змінного аргументу по замовчуванням без яких - або Heads-Up;)
schwobaseggl

1
Звичайно, але це не завадить і зважаючи на навчальний аспект питання, я вважаю це цілком доречним.
norok2

2
Це приклад з модуля бісектриси
dawg

@schwobaseggl навіть для таких невеликих списків бісект швидше. На моєму ноутбуці розчин бісект займає 1,2 мкс, а цикл займає 1,5 мкс
Іфтах

10

Ви можете зробити щось за цими напрямками:

# 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'

Для цього використовується nextаргумент за замовчуванням на генераторі над парами оцінок, створеними компанією zip. Це практично точний еквівалент вашого підходу до циклу.


5

Ви можете призначити кожному класу порогове значення:

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"

2
Зауважте, якщо ви використовуєте Python 3.6 або нижче, ви повинні зробити це, sorted(grades.items())оскільки дикти не гарантується їх сортування.
wjandrea

Це не буде надійно працювати у всіх версіях Python. Зауважте, що порядок диктату не гарантується. Також a dict- це надмірно важка структура даних, оскільки це має значення порядок, і ви все одно шукаєте за індексом (порядком), а не за ключем.
schwobaseggl

1
Звичайно, це не найефективніше, але це, мабуть, найбільш читабельно, оскільки всі позначки написані близько до їхнього порогу. Я вважаю за краще замінити дикт на пару з пар.
norok2

@schwobaseggl Для цього конкретного завдання, так, перелік кортежів був би кращим, ніж диктант, але якби весь цей код відбувався в модулі, то дікт дозволив би вам шукати літери клас -> поріг.
wjandrea

1
@wjandrea Якщо вам щось потрібно, вам потрібно буде поміняти ключі та значення, щоб дозволити щось на зразок grades[int(score*10)/10.0], але тоді вам слід скористатися Decimalяк плавці - це горезвісні недоброзичливі клавіші диктанту.
schwobaseggl

4

У цьому конкретному випадку вам не потрібні зовнішні модулі чи генератори. Деякої базової математики достатньо (і швидше)!

grades = ["A", "B", "C", "D", "F"]

def convert_score(score):
    return grades[-max(int(score * 10) - 5, 0) - 1]

# Examples:
print(convert_grade(0.61)) # "D"
print(convert_grade(0.37)) # "F"
print(convert_grade(0.94)) # "A"

2

Ви можете використовувати np.selectз бібліотеки numpy для кількох умов:

>> x = np.array([0.9,0.8,0.7,0.6,0.5])

>> conditions  = [ x >= 0.9,  x >= 0.8, x >= 0.7, x >= 0.6]
>> choices     = ['A','B','C','D']

>> np.select(conditions, choices, default='F')
>> array(['A', 'B', 'C', 'D', 'F'], dtype='<U1')

2

У мене є проста ідея, щоб вирішити це:

def convert_grade(numgrd):
    number = min(9, int(numgrd * 10))
    number = number if number >= 6 else 4
    return chr(74 - number)

Тепер,

print(convert_grade(.95))  # --> A 
print(convert_grade(.9))  # --> A
print(convert_grade(.4))  # --> F
print(convert_grade(.2))  # --> F

1

Ви можете використати numpy.searchsorted, що додатково надає вам цей хороший варіант обробки декількох балів за один виклик:

import numpy as np

grades = np.array(['F', 'D', 'C', 'B', 'A'])
thresholds = np.arange(0.6, 1, 0.1)

scores = np.array([0.75, 0.83, 0.34, 0.9])
grades[np.searchsorted(thresholds, scores)]  # output: ['C', 'B', 'F', 'A']

1

Ви подали простий випадок. Однак якщо ваша логіка ускладнюється, можливо, вам знадобиться система управління правилами для хаосу.

Ви можете спробувати движок Sauron Rule або знайти деякі двигуни Python від PYPI.


1
>>> grade = lambda score:'FFFFFFDCBAA'[int(score*100)//10]
>>> grade(0.8)
'B'

1
Хоча цей код може відповісти на питання, було б краще включити деякий контекст, пояснивши, як він працює і коли ним користуватися. Відповіді, що стосуються лише коду, з часом не корисні.
Мустафа

0

Ви також можете використовувати рекурсивний підхід:

grade_mapping = list(zip((0.9, 0.8, 0.7, 0.6, 0), 'ABCDF'))
def get_grade(score, index = 0):
    if score >= grade_mapping[index][0]:
        return(grade_mapping[index][1])
    else:
        return(get_grade(score, index = index + 1))

>>> print([get_grade(score) for score in [0, 0.59, 0.6, 0.69, 0.79, 0.89, 0.9, 1]])
['F', 'F', 'D', 'D', 'C', 'B', 'A', 'A']

0

Ось ще кілька стислих і менш зрозумілих підходів:

Перше рішення вимагає використання функції підлоги з mathбібліотеки.

from math import floor
def grade(mark):
    return ["D", "C", "B", "A"][min(floor(10 * mark - 6), 3)] if mark >= 0.6 else "F"

І якщо імпорт mathбібліотеки з якоїсь причини турбує вас. Ви можете використовувати обхід для функції підлоги:

def grade(mark):
    return ["D", "C", "B", "A"][min(int(10 * mark - 6) // 1, 3)] if mark >= 0.6 else "F"

Це трохи складніше, і я б радив їх використовувати, якщо ви не розумієте, що відбувається. Вони є специфічними рішеннями, які користуються тим фактом, що прирост у класах становить 0,1, що означає, що використання приросту, відмінного від 0,1, мабуть, не буде працювати за допомогою цієї методики. Він також не має простого інтерфейсу для відображення знаків для марок. Більш загальне рішення, як-от таке, що проводиться за допомогою бісекта, використовуючи бісект, мабуть, є більш підходящим чи дуже чистим рішенням schwobaseggl. Я не дуже впевнений, чому я публікую цю відповідь, але це лише спроба вирішити проблему без бібліотек (я не намагаюся сказати, що використання бібліотек погано) в одному рядку, що демонструє універсальний характер пітона.


0

Можна використовувати дикт.

Код

def grade(score):
    """Return a letter grade."""
    grades = {100: "A", 90: "A", 80: "B", 70: "C", 60: "D"}
    return grades.get((score // 10) * 10, "F")

Демо

[grade(scr) for scr in [100, 33, 95, 61, 77, 90, 89]]

# ['A', 'F', 'A', 'D', 'C', 'A', 'B']

Якщо бали дійсно від 0 до 1, спочатку помножте 100, а потім знайдіть бал.


0

Сподіваємось, що наступне може допомогти: якщо scr> = 0.9: print ('A') elif 0.9> scr> = 0.8: print ('B') elif 0.8> scr> = 0.7: Print ('C') elif 0.7 scr> = 0.6: друкувати ('D') інше: print ('F')


-3

У вас може бути список номерів, а потім список оцінок, які слід отримати з ним:

scores = (0.9, 0.8, 0.7, 0.6, 0.6)
lettergrades = ("A", "B", "C", "D", "F", "F")

Потім, якщо ви хочете перетворити заданий бал у літерний клас, ви можете зробити це:

item = 1 # Item 1 would be 0.8
scr = lettergrades[item]

Тоді ваш остаточний рахунок був би "B".


3
Якщо вам цікаво питання щодо телевізора: це рішення не дає можливості дістатись із оцінок, подібних 0.83до оцінки "B". Вам слід було б показати, як дістатись від балу до індексу item.
schwobaseggl
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.