编程风格和避免空值

val*_*age 1 python conditional for-loop function python-3.x

因此,我正在通过Wentworth等人如何像计算机科学家一样思考 Python 3指南,尝试更多地学习编程.虽然它是一个很棒的资源,但它对于在Python 3中编写的样式和"最佳实践"几乎没有什么可说的.

我正在编写有关条件的章节中的一个练习题,它要求我编写一个函数,当输入int或float'mark'时返回字符串'grade'.

我这里的直接问题是关于函数中条件的重复和函数返回的值.是否有可能以某种方式使用循环使其更简洁,而不是elif一遍又一遍地编写语句?此外,main grade函数返回null None值; 我怎样才能使这个功能"富有成效"而不是None在它被称为时打印?

这是我写的:

def grade(mark):
    grds = ['First','Upper Second','Second','Third','F1 Supp.','F2','F3']

    if mark >= 75.0:
        print("Your grade is",grds[0])
    elif mark < 75.0 and mark >= 70.0:
        print("Your grade is",grds[1])
    elif mark < 70.0 and mark >= 60.0:
        print("Your grade is",grds[2])
    elif mark < 60.0 and mark >= 50.0:
        print("Your grade is",grds[3])
    elif mark < 50.0 and mark >= 45.0:
        print("Your grade is",grds[4])
    elif mark < 45.0 and mark >= 40.0:
        print("Your grade is",grds[5])
    elif mark < 40.0: 
        print("Your grade is",grds[6])

def finalmark():
    mark = float(input("Enter your mark"))
    fnlmark = grade(mark)
    return fnlmark

print(finalmark())    
Run Code Online (Sandbox Code Playgroud)

Mar*_*ers 11

不是print()grade()函数中使用,而是返回结果并让调用者打印结果标记.该grade()功能仅用于返回成绩:

def grade(mark):
    grds = ['First','Upper Second','Second','Third','F1 Supp.','F2','F3']

    if mark >= 75.0:
        return grds[0]
    # .. etc

def finalmark():
    mark = float(input("Enter your mark"))
    fnlmark = grade(mark)
    print("Your grade is", fnlmark)

finalmark()
Run Code Online (Sandbox Code Playgroud)

注意,finalmark()现在负责打印; 这是最好的地方,因为同样的功能也负责在屏幕上打印问题并接受用户输入.与您的版本一样,finalmark()返回None(因为这是默认值),并且我print()finalmark()调用周围删除了以避免打印该返回值.打印它是没有意义的,finalmark()除了之外永远不会返回任何东西None.

您也可以删除一半的测试; 只挑选第一个匹配 ifelif分支,其余部分被跳过.因此,您可以删除先前分支已涵盖的测试:

def grade(mark):
    grds = ['First','Upper Second','Second','Third','F1 Supp.','F2','F3']

    if mark >= 75.0:
        return grds[0]
    elif mark >= 70.0:
        return grds[1]
    elif mark >= 60.0:
        return grds[2]
    elif mark >= 50.0:
        return grds[3]
    elif mark >= 45.0:
        return grds[4]
    elif mark >= 40.0:
        return grds[5]
    else:
        return grds[6]
Run Code Online (Sandbox Code Playgroud)

如果第一次if mark >= 75.0:测试不匹配,则不再需要测试mark < 75.0,因为我们已经测试了反向测试.测试对于mark >= 70.0下一年级来说已经足够了.如果不匹配,我们知道标记肯定小于70,所以下一个测试只需要测试它是否大于60.0等等.

现在出现了一种可以构建循环的模式.您测试下限,如果匹配,则知道要返回哪个索引.构建一个单独的列表来存储下限:

def grade(mark):
    grds = ['First','Upper Second','Second','Third','F1 Supp.','F2','F3']
    bounds = [75.0, 70.0, 60.0, 50.0, 45.0, 40.0]

    for grade, bound in zip(grds, bounds):
        if mark >= bound:
            return grade

    # there is no lower bound for F3; if the loop didn't find a match,
    # we end up here and can assume the lowest grade.
    return grds[6]
Run Code Online (Sandbox Code Playgroud)

我使用这里的zip()函数来配对成绩名称和边界,成对.您也可以使用该enumerate()函数生成索引以及每个成绩名称或for index in range(len(grds)):循环,但我发现zip()在这里工作更清洁.

接下来,我们可以开始聪明地使用算法.以上仍然逐一测试每个等级,从高到低.对于N个等级,这可能需要N个步骤.这是一个线性算法,它需要与输入一样多的步骤.

但是等级是分类的,所以我们可以在这里使用二分; 跳到中间,看看标记是低于还是高于当前界限.然后选择一半,再次测试,直到找到最佳匹配.Bisection最多采用Log(N)步.Python包含非常快速的实现 ; 它假定值按递增顺序排列,因此反转等级和边界:

import bisect

def grade(mark):
    grds = ['F3', 'F2', 'F1 Supp.', 'Third', 'Second', 'Upper Second', 'First']
    bounds = [40.0, 45.0, 50.0, 60.0, 70.0, 75.0]
    return grds[bisect.bisect_right(bounds, mark)]    
Run Code Online (Sandbox Code Playgroud)

bisect.bisect_right()一分为二bounds,找到"插入点" mark,它将位于列表中相同值的右侧.因此,35.0将插入at 0,50.0at 3(等于或更高),74.0at 5和at 75.0或更高处6.而那些恰好是匹配等级的确切指数.