Bagaimana saya bisa menyederhanakan pernyataan if-elif berulang dalam fungsi sistem penilaian saya?

20

Tujuannya adalah untuk membangun program untuk mengonversi skor dari sistem '0 ke 1' ke sistem 'F ke A':

  • Jika score >= 0.9mau mencetak 'A'
  • Jika score >= 0.8mau mencetak 'B'
  • 0,7, C
  • 0,6, D
  • Dan nilai apa pun di bawah titik itu, cetak F

Ini adalah cara untuk membuatnya dan bekerja pada program, tetapi ini agak berulang:

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

Saya ingin tahu apakah ada cara untuk membangun fungsi sehingga pernyataan majemuk tidak akan berulang.

Saya seorang pemula total, tetapi akankah ada sesuatu di baris:

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

menjadi mungkin?

Maksudnya di sini adalah bahwa nanti kita bisa menyebutnya dengan hanya meneruskan scr, numbergrade, dan grade surat sebagai argumen:

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

Jika dimungkinkan untuk mengeluarkan lebih sedikit argumen, itu akan lebih baik.

Matheus Bezerra Soares
sumber
2
Apakah ini menjawab pertanyaan Anda? Bagaimana cara membuat sistem penilaian dalam python?
RoadRunner

Jawaban:

31

Anda dapat menggunakan modul bisect untuk melakukan pencarian tabel numerik:

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']
dawg
sumber
2
Saya ingin memiliki +1 tambahan untuk penggunaannya bisect, yang menurut saya terlalu jarang digunakan.
norok2
4
@ norok2 Saya tidak berpikir daftar 4 elemen adalah tempat untuk memulai. Untuk daftar sekecil itu, pemindaian linear kemungkinan akan lebih cepat. Ditambah penggunaan argumen default yang bisa berubah tanpa kepala;)
schwobaseggl
1
Tentu, tetapi tidak sakit dan mengingat aspek pembelajaran dari pertanyaan itu, saya merasa cukup tepat.
norok2
2
Ini adalah contoh dari modul bisect
dawg
@schwobaseggl bahkan untuk daftar kecil seperti itu lebih cepat. Di laptop saya solusi bisect mengambil 1,2μs dan loop mengambil 1,5μs
Iftah
10

Anda dapat melakukan sesuatu seperti ini:

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

Ini digunakan nextdengan argumen default pada generator di atas pasangan skor-skor yang dibuat oleh zip. Ini sebenarnya setara dengan pendekatan loop Anda.

schwobaseggl
sumber
5

Anda dapat menetapkan nilai ambang untuk setiap nilai:

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"
Nico
sumber
2
Catatan, jika Anda menggunakan Python 3.6 atau di bawah, Anda harus melakukannya sorted(grades.items())karena dicts tidak dijamin akan diurutkan.
wjandrea
Ini tidak akan andal bekerja di semua versi Python. Perhatikan bahwa urutan dikte tidak dijamin. Juga a dictadalah struktur data berat yang tidak perlu, karena itu urutan yang penting, dan Anda mencari berdasarkan indeks (pesanan), bukan dengan kunci.
schwobaseggl
1
Tentu bukan yang paling efisien, tetapi bisa dibilang yang paling mudah dibaca karena semua tanda ditulis mendekati ambangnya. Saya lebih suka mengganti dict dengan tuple of pair.
norok2
@schwobaseggl Untuk tugas khusus ini, ya, daftar tupel akan lebih baik daripada dict, tetapi jika semua kode ini masuk dalam modul, dict akan memungkinkan Anda untuk mencari nilai huruf -> ambang batas.
wjandrea
1
@wjandrea Jika ada, Anda harus menukar kunci dan nilai untuk mengizinkan sesuatu seperti grades[int(score*10)/10.0], tetapi kemudian Anda harus menggunakannya Decimalkarena float adalah kunci dikt yang terkenal buruk.
schwobaseggl
5

Dalam kasus khusus ini Anda tidak perlu modul atau generator eksternal. Beberapa matematika dasar sudah cukup (dan lebih cepat)!

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"
Riccardo Bucco
sumber
2

Anda dapat menggunakan np.selectdari numpy library untuk berbagai kondisi:

>> 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')
YOLO
sumber
2

Saya punya ide sederhana untuk menyelesaikan ini:

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

Sekarang,

print(convert_grade(.95))  # --> A 
print(convert_grade(.9))  # --> A
print(convert_grade(.4))  # --> F
print(convert_grade(.2))  # --> F
Islam Taohidul
sumber
1

Anda dapat menggunakan numpy.searchsorted, yang juga memberi Anda opsi bagus untuk memproses beberapa skor dalam satu panggilan:

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']
mysh
sumber
1

Anda memberikan kasus sederhana. Namun jika logika Anda semakin rumit, Anda mungkin memerlukan mesin aturan untuk menangani kekacauan.

Anda dapat mencoba mesin Sauron Rule atau menemukan beberapa mesin aturan Python dari PYPI.

Leon
sumber
1
>>> grade = lambda score:'FFFFFFDCBAA'[int(score*100)//10]
>>> grade(0.8)
'B'
Biar Fordlander
sumber
1
Meskipun kode ini dapat menjawab pertanyaan, akan lebih baik untuk memasukkan beberapa konteks, menjelaskan cara kerjanya dan kapan menggunakannya. Jawaban kode saja tidak berguna dalam jangka panjang.
Mustafa
0

Anda juga bisa menggunakan pendekatan rekursif:

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']
Palu
sumber
0

Berikut adalah beberapa pendekatan yang lebih ringkas dan kurang dimengerti:

Solusi pertama membutuhkan penggunaan fungsi lantai dari mathperpustakaan.

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

Dan jika karena alasan tertentu mengimpor mathperpustakaan mengganggu Anda. Anda bisa menggunakan pekerjaan di sekitar untuk fungsi lantai:

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

Ini agak rumit dan saya akan menyarankan untuk tidak menggunakannya kecuali Anda mengerti apa yang sedang terjadi. Mereka adalah solusi spesifik yang mengambil keuntungan dari fakta bahwa kenaikan nilai adalah 0,1 yang berarti bahwa menggunakan kenaikan selain 0,1 mungkin tidak akan berhasil menggunakan teknik ini. Itu juga tidak memiliki antarmuka yang mudah untuk memetakan tanda ke nilai. Solusi yang lebih umum seperti solusi dawg menggunakan bisect mungkin lebih tepat atau solusi schwobaseggl yang sangat bersih. Saya tidak benar-benar yakin mengapa saya memposting jawaban ini tetapi itu hanya upaya memecahkan masalah tanpa perpustakaan (saya tidak mencoba mengatakan bahwa menggunakan perpustakaan itu buruk) dalam satu baris menunjukkan sifat serbaguna python.

Fizzlebert
sumber
0

Anda dapat menggunakan dict.

Kode

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

Jika skor sebenarnya antara 0 dan 1, kalikan pertama 100, lalu cari skor.

pylang
sumber
0

Semoga yang berikut dapat membantu: jika scr> = 0,9: print ('A') elif 0,9> scr> = 0,8: print ('B') elif 0,8> scr> = 0,7: Cetak ('C') elif 0,7 scr> = 0.6: cetak ('D') lain: cetak ('F')

Amanda
sumber
-3

Anda bisa memiliki daftar angka, lalu daftar nilai yang menyertainya:

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

Kemudian, jika Anda ingin mengonversi skor yang ditentukan ke nilai huruf, Anda bisa melakukan ini:

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

Maka skor akhir Anda akan menjadi "B".

GavinTheCrafter
sumber
3
Jika Anda bertanya-tanya tentang dv's: solusi ini tidak memberikan cara untuk mendapatkan dari skor suka 0.83ke kelas "B". Anda harus menunjukkan cara mendapatkan dari skor ke indeks item.
schwobaseggl