Derecelendirme sistemi işlevimde yinelenen if-elif deyimlerini nasıl basitleştirebilirim?


20

Amaç, skorları '0'dan 1'e' sistemden 'F'den A'ya' sistemine dönüştürmek için bir program oluşturmaktır:

  • Eğer score >= 0.9'A' basacaktır
  • Eğer score >= 0.8'B' basacaktır
  • 0.7, C
  • 0.6, D
  • Ve bu noktanın altındaki herhangi bir değer, F

Bu onu oluşturmanın yolu ve program üzerinde çalışıyor, ancak biraz tekrarlı:

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

Bileşik ifadeler tekrarlayan olmayacak şekilde bir işlev oluşturmak için bir yol olup olmadığını bilmek istiyorum.

Ben tam bir acemi değilim, ama satırında bir şey olurdu:

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

mümkün mü?

Buradaki amaç, daha sonra sadece scr, numbergrade ve letter notlarını argümanlar olarak ileterek arayabileceğimizdir:

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

Daha az argüman iletmek mümkün olsaydı, daha da iyi olurdu.



Yanıtlar:


30

Sayısal tablo araması yapmak için bisect modülünü kullanabilirsiniz :

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
bisectÇok nadir kullanıldığını düşündüğüm kullanım için ek bir +1 almak istiyorum.
norok2

4
@ norok2 4 öğeden oluşan bir listenin başlangıç ​​için bir yer olduğunu sanmıyorum. Bu tür küçük listeler için doğrusal bir tarama muhtemelen daha hızlı olacaktır. Ayrıca herhangi bir başlık olmadan değiştirilebilir varsayılan bir argümanın kullanımı;)
schwobaseggl

1
Tabii, ama acıtmıyor ve sorunun öğrenme yönü göz önüne alındığında, oldukça uygun buluyorum.
norok2

2
İki bölümlü modülden örnek
dawg

@schwobaseggl bile böyle küçük listeler için bisect daha hızlıdır. Dizüstü bilgisayarımda bisect çözümü 1.2µs ve döngü 1.5µs alır
Iftah

10

Bu çizgiler boyunca bir şeyler yapabilirsiniz:

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

Bu kullanır next tarafından oluşturulmuş skor-dereceli çiftler üzerinde bir jeneratörde varsayılan bir argüman ile kullanılır zip. Bu, neredeyse döngü yaklaşımınızın tam eşdeğeridir.


5

Her dereceye bir eşik değeri atayabilirsiniz:

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 veya daha eski bir sorted(grades.items())sürümü kullanıyorsanız, diktelerin sıralanacağı garanti edilmediğinden yapmanız gerekir .
wjandrea

Bu, tüm Python sürümlerinde güvenilir bir şekilde çalışmaz. Bir kararın sırasının garanti edilmediğini unutmayın. Ayrıca a dict, gereksiz olan ağır bir veri yapısıdır, çünkü önemli olan düzendir ve anahtarla değil, yine de dizin (sipariş) ile bakıyorsunuz.
schwobaseggl

1
Tabii ki en verimli değil, ancak tüm işaretler eşiklerine yakın yazıldığından tartışmasız en okunabilir. Dict'i bir çift çiftle değiştirmeyi tercih ederim.
norok2

@schwobaseggl Bu özel görev için, evet, bir tuples listesi bir diksiyondan daha iyi olurdu, ancak tüm bu kod bir modüle gidiyorsa, diksiyon harf notu -> eşiğini aramanıza izin verir.
wjandrea

1
@wjandrea Herhangi bir şey varsa grades[int(score*10)/10.0], bunun gibi bir şeye izin vermek için anahtarları ve değerleri değiştirmeniz gerekir , ancak o zaman Decimalşamandıralar kötü niyetli kötü niyetli dikte anahtarları olarak kullanmalısınız.
schwobaseggl

5

Bu özel durumda harici modüllere veya jeneratörlere ihtiyacınız yoktur. Bazı temel matematik yeterlidir (ve daha hızlı)!

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.selectBirden çok koşul için numpy kütüphanesinden kullanabilirsiniz :

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

Bunu çözmek için basit bir fikrim var:

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

Şimdi,

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

1

numpy.searchsortedAyrıca, tek bir çağrıda birden çok puanı işlemek için bu güzel seçeneği sunan kullanabilirsiniz :

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

Basit bir dava sağladınız. Ancak mantığınız daha karmaşık hale geliyorsa, kaosun üstesinden gelmek için kural motoruna .

Sauron Kural motorunu deneyebilir veya PYPI'den bazı Python kural motorları bulabilirsiniz.


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

1
Bu kod soruyu cevaplayabilirken, nasıl çalıştığını ve ne zaman kullanılacağını açıklayan bir bağlam eklemek daha iyi olur. Yalnızca kod yanıtları uzun vadede yararlı değildir.
Mustafa

0

Özyinelemeli bir yaklaşım da kullanabilirsiniz:

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

İşte bazı daha özlü ve daha az anlaşılabilir yaklaşımlar:

İlk çözüm, zemin fonksiyonunun mathkütüphaneden kullanılmasını gerektirir .

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

Ve herhangi bir nedenle mathkütüphaneyi içe aktarmak sizi rahatsız ediyorsa. Zemin fonksiyonu için bir çalışma kullanabilirsiniz:

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

Bunlar biraz karmaşık ve neler olup bittiğini anlamadığınız sürece bunları kullanmamanızı tavsiye ederim. Bunlar, derecelerdeki artışların 0.1 olması gerçeğinden yararlanan özel çözümlerdir, yani 0.1 dışında bir artış kullanmanın muhtemelen bu teknik kullanılarak işe yaramayacağı anlamına gelir. Ayrıca notları notlarla eşlemek için kolay bir arayüze sahip değildir. Bisect kullanarak dawg tarafından yapılan gibi daha genel bir çözüm muhtemelen daha uygun veya schwobaseggl'in çok temiz çözeltisidir. Neden bu cevabı gönderiyorum gerçekten emin değilim ama sadece herhangi bir kütüphane olmadan sorunu çözmek için bir girişim (kütüphaneleri kullanmanın kötü olduğunu söylemeye çalışmıyorum) python çok yönlü doğasını gösteren.


0

Bir diksiyon kullanabilirsiniz.

kod

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

gösteri

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

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

Skorlar aslında 0 ile 1 arasındaysa, önce 100 ile çarpın, sonra skoru arayın.


0

Aşağıdaki umut yardımcı olabilir: eğer scr> = 0.9: baskı ('A') elif 0.9> scr> = 0.8: baskı ('B') elif 0.8> scr> = 0.7: Baskı ('C') elif 0.7 scr> = 0.6: baskı ( 'D') başka bir: baskı ( 'F')


-3

Bir sayı listesi, ardından onunla gitmek için notların bir listesi olabilir:

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

Ardından, belirtilen bir puanı bir harf notuna dönüştürmek istiyorsanız, bunu yapabilirsiniz:

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

O zaman son skorunuz olur "B".


3
DV'leri merak ediyorsanız: Bu çözüm 0.83, sınıfa benzer bir puan almak için hiçbir yol sağlamaz "B". Skordan endekse nasıl ulaşacağınızı göstermelisiniz item.
schwobaseggl
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.