Wie kann ich sich wiederholende if-elif-Anweisungen in meiner Bewertungssystemfunktion vereinfachen?


20

Das Ziel ist es, ein Programm zu erstellen, um Punktzahlen von einem '0 zu 1'-System in ein' F zu A'-System umzuwandeln:

  • Wenn score >= 0.9 würde 'A' drucken
  • Wenn score >= 0.8 würde 'B' drucken
  • 0,7, C.
  • 0,6, D.
  • Und jeder Wert unter diesem Punkt druckt F.

Dies ist der Weg, um es zu erstellen und es funktioniert auf dem Programm, aber es ist etwas repetitiv:

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

Ich würde gerne wissen, ob es eine Möglichkeit gibt, eine Funktion so zu erstellen, dass sich die zusammengesetzten Anweisungen nicht so oft wiederholen.

Ich bin ein absoluter Anfänger, würde aber etwas in der Art von:

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

möglich sein?

Die Absicht hier ist, dass wir es später aufrufen können, indem wir nur scr, numbergrade und letter grade als Argumente übergeben:

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

Wenn es möglich wäre, weniger Argumente zu übergeben, wäre es sogar noch besser.



Antworten:


30

Mit dem Halbierungsmodul können Sie eine numerische Tabellensuche durchführen:

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
Ich hätte gerne eine zusätzliche +1 für die Verwendung bisect, die ich zu selten finde.
norok2

4
@ norok2 Ich glaube nicht, dass eine Liste mit 4 Elementen der Ausgangspunkt ist. Bei solch kleinen Listen ist ein linearer Scan wahrscheinlich schneller. Plus die Verwendung eines veränderlichen Standardarguments ohne Heads-up;)
schwobaseggl

1
Sicher, aber es tut nicht weh und angesichts des Lernaspekts der Frage finde ich es durchaus angemessen.
norok2

2
Es ist das Beispiel aus dem
Halbierungsmodul

@schwobaseggl auch bei so kleinen Listen ist die Halbierung schneller. Auf meinem Laptop dauert die Halbierungslösung
1,2

10

Sie können etwas in diese Richtung tun:

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

Dies wird nextmit einem Standardargument für einen Generator über die von erstellten Score-Grade-Paare verwendet zip. Es ist praktisch das genaue Äquivalent Ihres Loop-Ansatzes.


5

Sie können jeder Note einen Schwellenwert zuweisen:

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
Hinweis: Wenn Sie Python 3.6 oder niedriger verwenden, sollten Sie dies tun, sorted(grades.items())da die Sortierung von Diktaten nicht garantiert ist.
wjandrea

Dies funktioniert nicht in allen Python-Versionen zuverlässig. Beachten Sie, dass die Reihenfolge eines Diktats nicht garantiert ist. Außerdem dictist a eine unnötig schwere Datenstruktur, da es auf die Reihenfolge ankommt und Sie ohnehin nach Index (Reihenfolge) und nicht nach Schlüssel suchen.
schwobaseggl

1
Sicher ist es nicht das effizienteste, aber es ist wohl das am besten lesbare, da alle Markierungen nahe an ihrer Schwelle geschrieben sind. Ich würde eher vorschlagen, das Diktat durch ein Tupel von Paaren zu ersetzen.
norok2

@schwobaseggl Für diese spezielle Aufgabe wäre eine Liste von Tupeln besser als ein Diktat, aber wenn all dieser Code in einem Modul enthalten wäre, würde das Diktat es Ihnen ermöglichen, die Briefnote -> Schwelle nachzuschlagen.
Wjandrea

1
@wjandrea Wenn überhaupt, müssten Sie Schlüssel und Werte austauschen, um so etwas zuzulassen grades[int(score*10)/10.0], aber dann sollten Sie Decimalals Floats notorisch schlecht benommene Diktatschlüssel verwenden.
schwobaseggl

5

In diesem speziellen Fall benötigen Sie keine externen Module oder Generatoren. Einige grundlegende Mathematik ist genug (und schneller)!

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

Sie können np.selectaus der Numpy-Bibliothek für mehrere Bedingungen verwenden:

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

Ich habe eine einfache Idee, um dies zu lösen:

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

Jetzt,

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

1

Sie können Folgendes verwenden numpy.searchsorted, wodurch Sie zusätzlich die Möglichkeit haben, mehrere Ergebnisse in einem einzigen Anruf zu verarbeiten:

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

Sie haben einen einfachen Fall angegeben. Wenn Ihre Logik jedoch komplizierter wird, benötigen Sie möglicherweise eine Regelengine , um das Chaos zu bewältigen.

Sie können die Sauron Rule Engine ausprobieren oder einige Python Rules Engines von PYPI finden.


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

1
Während dieser Code die Frage beantworten kann, ist es besser, einen Kontext einzuschließen, in dem erklärt wird, wie er funktioniert und wann er verwendet werden soll. Nur-Code-Antworten sind auf lange Sicht nicht sinnvoll.
Mustafa

0

Sie können auch einen rekursiven Ansatz verwenden:

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

Hier sind einige prägnantere und weniger verständliche Ansätze:

Die erste Lösung erfordert die Verwendung der Bodenfunktion aus der mathBibliothek.

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

Und wenn Sie aus irgendeinem Grund beim Importieren der mathBibliothek gestört werden. Sie könnten eine Problemumgehung für die Bodenfunktion verwenden:

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

Diese sind etwas kompliziert und ich würde davon abraten, sie zu verwenden, es sei denn, Sie verstehen, was los ist. Dies sind spezifische Lösungen, die die Tatsache ausnutzen, dass die Inkremente in Noten 0,1 betragen, was bedeutet, dass die Verwendung eines anderen Inkrements als 0,1 mit dieser Technik wahrscheinlich nicht funktionieren würde. Es gibt auch keine einfache Oberfläche zum Zuordnen von Noten zu Noten. Eine allgemeinere Lösung wie die von dawg mit Bisect ist wahrscheinlich besser geeignet oder die sehr saubere Lösung von schwobaseggl. Ich bin mir nicht sicher, warum ich diese Antwort veröffentliche, aber es ist nur ein Versuch, das Problem ohne Bibliotheken zu lösen (ich versuche nicht zu sagen, dass die Verwendung von Bibliotheken schlecht ist) in einer Zeile, die die Vielseitigkeit von Python demonstriert.


0

Sie können ein Diktat verwenden.

Code

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

Demo

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

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

Wenn die Punktzahl tatsächlich zwischen 0 und 1 liegt, multiplizieren Sie zuerst 100 und suchen Sie dann die Punktzahl.


0

Hoffe, dass Folgendes helfen könnte: Wenn 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: print ('D') else: print ('F')


-3

Sie könnten eine Liste mit Zahlen und dann eine Liste mit Noten haben:

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

Wenn Sie dann eine bestimmte Punktzahl in eine Buchstabennote umwandeln möchten, können Sie Folgendes tun:

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

Dann wäre Ihr Endergebnis "B".


3
Für den Fall, dass Sie sich über die DVDs wundern: Diese Lösung bietet keine Möglichkeit, von einer Punktzahl wie 0.83der Note zu kommen "B". Sie müssten zeigen, wie Sie von der Partitur zum Index gelangen item.
schwobaseggl
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.