ฉันจะลดความซับซ้อนของคำสั่ง if-elif ซ้ำ ๆ ในฟังก์ชั่นระบบการให้เกรดของฉันได้อย่างไร


20

เป้าหมายคือการสร้างโปรแกรมเพื่อแปลงคะแนนจากระบบ '0 ถึง 1' เป็นระบบ 'F ถึง A':

  • ถ้าscore >= 0.9จะพิมพ์ 'A'
  • ถ้าscore >= 0.8จะพิมพ์ 'B'
  • 0.7, C
  • 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
สิ่งนี้ตอบคำถามของคุณหรือไม่ วิธีการสร้างระบบการให้เกรดในหลาม?
RoadRunner

คำตอบ:


30

คุณสามารถใช้โมดูลbisectเพื่อค้นหาตารางตัวเลข:

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 เป็นจุดเริ่มต้นแม้ว่า สำหรับรายการขนาดเล็กเช่นการสแกนเชิงเส้นมีแนวโน้มที่จะเร็วขึ้น รวมถึงการใช้อาร์กิวเมนต์เริ่มต้นที่ไม่แน่นอนโดยไม่ต้องมีหัว);
schwobaseggl

1
แน่นอน แต่มันไม่เจ็บและให้มุมมองการเรียนรู้ของคำถามฉันคิดว่ามันค่อนข้างเหมาะสม
norok2

2
มันเป็นตัวอย่างจากโมดูล bisect
dawg

@schwobaseggl แม้รายการขนาดเล็กเช่นนี้จะแบ่งออกเป็นสองส่วนได้เร็วขึ้น ในแล็ปท็อปของฉันโซลูชัน bisect ใช้เวลา 1.2 วินาทีและลูปใช้เวลา 1.5 วินาที
Iftah

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'

สิ่งนี้ใช้ nextzipมีการโต้เถียงเริ่มต้นในการกำเนิดมากกว่าคู่คะแนนเกรดที่สร้างขึ้นโดย มันเกือบเทียบเท่ากับวิธีวนรอบที่แน่นอนของคุณ


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 โปรดทราบว่าคำสั่งของ dict ไม่ได้รับประกัน นอกจากนี้ a ยังเป็นdictโครงสร้างข้อมูลที่มีน้ำหนักมากโดยไม่จำเป็นเนื่องจากมันเป็นคำสั่งที่สำคัญและคุณค้นหาโดยใช้ดัชนี (คำสั่งซื้อ) ต่อไปไม่ใช่โดยใช้คีย์
schwobaseggl

1
แน่นอนว่าไม่ได้มีประสิทธิภาพมากที่สุด แต่เป็นเนื้อหาที่อ่านง่ายที่สุดเนื่องจากเครื่องหมายทั้งหมดถูกเขียนใกล้เคียงกับเพดาน ฉันอยากแนะนำให้แทนที่ dict เป็น tuple เป็นคู่
norok2

@schwobaseggl สำหรับงานเฉพาะนี้ใช่รายการของ tuples จะดีกว่า dict แต่ถ้ารหัสทั้งหมดนี้ไปในโมดูล dict จะอนุญาตให้คุณค้นหาเกรดตัวอักษร -> threshold
wjandrea

1
@wjandrea หากมีสิ่งใดที่คุณจะต้องสลับคีย์และค่าเพื่ออนุญาตบางอย่างเช่นgrades[int(score*10)/10.0]แต่คุณควรใช้Decimalเนื่องจากการลอยนั้นเป็นคีย์ดิคที่มีพฤติกรรมไม่ดี
schwobaseggl

5

ในกรณีเฉพาะนี้คุณไม่ต้องการโมดูลหรือเครื่องกำเนิดไฟฟ้าภายนอก คณิตศาสตร์พื้นฐานบางอย่างก็เพียงพอ (และเร็วกว่า)!

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จากห้องสมุดที่มีหลายเงื่อนไข:

>> 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 engineหรือค้นหา Python rule engine จาก 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 อาจจะไม่สามารถใช้งานได้โดยใช้เทคนิคนี้ นอกจากนี้ยังไม่มีอินเทอร์เฟซที่ง่ายสำหรับการทำเครื่องหมายเครื่องหมายกับคะแนน วิธีแก้ปัญหาทั่วไปที่มากขึ้นเช่นทีละน้อยโดยใช้ bisect น่าจะเหมาะสมกว่าหรือวิธีแก้ปัญหาที่สะอาดมากของ schwobaseggl ฉันไม่แน่ใจจริงๆว่าทำไมฉันโพสต์คำตอบนี้ แต่มันเป็นเพียงความพยายามในการแก้ปัญหาโดยไม่ต้องมีห้องสมุดใด ๆ (ฉันไม่ได้พยายามที่จะพูดว่าการใช้ห้องสมุดไม่ดี) ในหนึ่งบรรทัดแสดงให้เห็นถึงลักษณะที่หลากหลายของงูหลาม


0

คุณสามารถใช้ dict

รหัส

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

หวังว่าสิ่งต่อไปนี้อาจช่วยได้: if 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') อื่น ๆ : การพิมพ์ ( '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
ในกรณีที่คุณสงสัยเกี่ยวกับ DV ที่คล้ายกัน: การแก้ปัญหานี้มีวิธีที่จะได้รับจากคะแนนไม่ชอบที่จะคะแนน0.83 คุณจะต้องแสดงให้เห็นว่าจะได้รับจากการให้คะแนนดัชนี"B" item
schwobaseggl
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.