Làm thế nào tôi có thể đơn giản hóa các câu lệnh if-elif lặp đi lặp lại trong chức năng hệ thống phân loại của mình?


20

Mục tiêu là xây dựng một chương trình để chuyển đổi điểm số từ hệ thống '0 thành 1' sang hệ thống 'F thành A':

  • Nếu score >= 0.9sẽ in 'A'
  • Nếu score >= 0.8sẽ in 'B'
  • 0,7, C
  • 0,6, D
  • Và bất kỳ giá trị nào dưới điểm đó, in F

Đây là cách để xây dựng nó và nó hoạt động trên chương trình, nhưng nó hơi lặp đi lặp lại:

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

Tôi muốn biết nếu có một cách để xây dựng một hàm sao cho các câu lệnh ghép sẽ không lặp lại.

Tôi là một người mới bắt đầu, nhưng sẽ có thứ gì đó trong dòng:

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

có khả năng?

Ý định ở đây là sau này chúng ta có thể gọi nó bằng cách chỉ chuyển các lớp Scr, số và chữ cái làm đối số:

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

Nếu nó có thể vượt qua ít đối số hơn, nó sẽ còn tốt hơn nữa.



2
Điều này có trả lời câu hỏi của bạn không? Làm thế nào để tạo một hệ thống phân loại trong python?
RoadRunner

Câu trả lời:


30

Bạn có thể sử dụng mô-đun bisect để thực hiện tra cứu bảng số:

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
Tôi muốn có thêm +1 cho việc sử dụng bisect, điều mà tôi thấy nó hiếm khi được sử dụng.
norok2

4
@ norok2 Tôi không nghĩ rằng một danh sách gồm 4 yếu tố là nơi để bắt đầu. Đối với các danh sách nhỏ như vậy, quét tuyến tính có thể sẽ nhanh hơn. Cộng với việc sử dụng một đối số mặc định có thể thay đổi mà không cần bất kỳ sự hỗ trợ nào;)
schwobaseggl

1
Chắc chắn, nhưng nó không làm tổn thương và đưa ra khía cạnh học tập của câu hỏi, tôi thấy nó khá phù hợp.
norok2

2
Đây là ví dụ từ mô-đun bisect
dawg

@schwobaseggl ngay cả đối với các danh sách nhỏ như vậy, bisect sẽ nhanh hơn. Trên máy tính xách tay của tôi, giải pháp bisect mất 1,2 Lời và vòng lặp mất 1,5 Lời
Iftah

10

Bạn có thể làm một cái gì đó dọc theo những dòng này:

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

Điều này sử dụng nextvới một đối số mặc định trên một trình tạo trên các cặp điểm số được tạo bởi zip. Nó gần như tương đương chính xác với cách tiếp cận vòng lặp của bạn.


5

Bạn có thể gán cho mỗi lớp một giá trị ngưỡng:

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
Lưu ý, nếu bạn đang sử dụng Python 3.6 trở xuống, bạn nên làm sorted(grades.items())vì các lệnh không đảm bảo được sắp xếp.
wjandrea

Điều này sẽ không hoạt động đáng tin cậy trong tất cả các phiên bản Python. Lưu ý rằng thứ tự của một dict không được đảm bảo. Ngoài ra, a dictlà một cấu trúc dữ liệu nặng không cần thiết, vì đó là thứ tự quan trọng và dù sao bạn cũng đang tìm kiếm theo chỉ mục (thứ tự) chứ không phải bằng khóa.
schwobaseggl

1
Chắc chắn không phải là hiệu quả nhất, nhưng nó được cho là dễ đọc nhất vì tất cả các nhãn hiệu được viết gần với ngưỡng của chúng. Tôi muốn đề nghị thay thế dict bằng một cặp.
norok2

@schwobaseggl Đối với tác vụ cụ thể này, vâng, một danh sách các bộ dữ liệu sẽ tốt hơn một lệnh, nhưng nếu tất cả mã này đang diễn ra trong một mô-đun, chính tả sẽ cho phép bạn tra cứu lớp thư -> ngưỡng.
wjandrea

1
@wjandrea Nếu có bất cứ điều gì, bạn cần trao đổi khóa và giá trị để cho phép một cái gì đó giống như grades[int(score*10)/10.0], nhưng sau đó bạn nên sử dụng Decimalvì phao là các khóa chính tả hành vi xấu.
schwobaseggl

5

Trong trường hợp cụ thể này, bạn không cần mô-đun bên ngoài hoặc máy phát điện. Một số toán cơ bản là đủ (và nhanh hơn)!

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

Bạn có thể sử dụng np.selecttừ thư viện numpy cho nhiều điều kiện:

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

Tôi có một ý tưởng đơn giản để giải quyết điều này:

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

Hiện nay,

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

1

Bạn có thể sử dụng numpy.searchsorted, cung cấp cho bạn tùy chọn tuyệt vời này để xử lý nhiều điểm số trong một cuộc gọi:

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

Bạn đã cung cấp một trường hợp đơn giản. Tuy nhiên, nếu logic của bạn trở nên phức tạp hơn, bạn có thể cần một công cụ quy tắc để xử lý sự hỗn loạn.

Bạn có thể thử công cụ Quy tắc Sauron hoặc tìm một số công cụ quy tắc Python từ PYPI.


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

1
Mặc dù mã này có thể trả lời câu hỏi, nhưng sẽ tốt hơn nếu bao gồm một số bối cảnh, giải thích cách thức hoạt động và khi nào sử dụng nó. Câu trả lời chỉ có mã là không hữu ích trong thời gian dài.
Mustafa

0

Bạn cũng có thể sử dụng một cách tiếp cận đệ quy:

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

Dưới đây là một số cách tiếp cận ngắn gọn và dễ hiểu hơn:

Giải pháp đầu tiên yêu cầu sử dụng chức năng sàn từ maththư viện.

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

Và nếu vì lý do nào đó, việc nhập maththư viện đang làm phiền bạn. Bạn có thể sử dụng một công việc xung quanh cho chức năng sàn:

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

Đây là một chút phức tạp và tôi sẽ khuyên bạn không nên sử dụng chúng trừ khi bạn hiểu những gì đang xảy ra. Chúng là các giải pháp cụ thể lợi dụng thực tế là số gia trong các lớp là 0,1 có nghĩa là sử dụng số gia khác 0,1 có thể không hoạt động khi sử dụng kỹ thuật này. Nó cũng không có giao diện dễ dàng để ánh xạ các dấu tới các lớp. Một giải pháp tổng quát hơn như giải pháp của dawg sử dụng bisect có lẽ là giải pháp rất sạch sẽ hoặc phù hợp hơn của schwobaseggl. Tôi không thực sự chắc chắn tại sao tôi đăng câu trả lời này nhưng đó chỉ là một nỗ lực giải quyết vấn đề mà không có bất kỳ thư viện nào (tôi không cố nói rằng sử dụng thư viện là xấu) trong một dòng thể hiện bản chất linh hoạt của python.


0

Bạn có thể sử dụng một 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")

Bản giới thiệu

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

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

Nếu điểm thực sự nằm trong khoảng từ 0 đến 1, trước tiên hãy nhân 100, sau đó tra cứu điểm số.


0

Hy vọng sau đây có thể giúp: if Scr> = 0.9: print ('A') elif 0.9> Scr> = 0.8: print ('B') elif 0.8> Scr> = 0.7: In ('C') elif 0.7 Scr> = 0,6: in ('D') khác: in ('F')


-3

Bạn có thể có một danh sách các số, sau đó là danh sách các lớp đi kèm với nó:

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

Sau đó, nếu bạn muốn chuyển đổi một số điểm được chỉ định thành một lớp chữ cái, bạn có thể làm điều này:

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

Sau đó, điểm số cuối cùng của bạn sẽ là "B".


3
Trong trường hợp bạn đang tự hỏi về dv của: giải pháp này cung cấp không có cách nào để có được từ một số điểm như 0.83đến lớp "B". Bạn sẽ phải chỉ ra làm thế nào để có được từ điểm số đến chỉ số item.
schwobaseggl
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.