编程风格和避免空值

2024-10-08 18:30:45 发布

您现在位置:Python中文网/ 问答频道 /正文

因此,我正在温特沃斯等人的python3指南中学习如何教自己更多的编程知识。虽然它是一个非常棒的资源,但是对于用python3编写的风格和“最佳实践”却几乎没有什么可说的。你知道吗

我正在研究条件句一章中的一个练习问题,它要求我编写一个函数,在输入int或float“mark”时返回字符串“grade”。你知道吗

我这里的直接问题是关于函数中条件的重复和函数返回的值。有没有可能使用循环而不是反复编写elif语句,从而使循环更加简洁?另外,主grade函数返回一个空的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())    

Tags: and函数noneyourisdeffloatpython3
3条回答

与其在grade()函数中使用print()返回结果并让调用者打印结果标记。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()

请注意,finalmark()现在负责打印;这是它的最佳位置,因为同样的函数也负责在屏幕上打印问题并接受用户输入。与您的版本一样,finalmark()返回None(因为这是默认值),我从finalmark()调用中删除了print(),以避免打印返回值。打印它没有任何意义,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]

如果第一个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]

我用这里的^{} function将等级名称和界限成对排列。您也可以使用^{} function来生成一个索引以及每个年级名称,或者一个for index in range(len(grds)):循环,但是我发现zip()在这里更有效。你知道吗

下一步,我们可以开始聪明的算法。以上仍然测试每个年级,从高到低,一个接一个。对于N个等级,最多需要N个步骤。这是一个线性算法,它需要的步骤和输入的步骤一样多。你知道吗

但是等级是排序的,所以我们可以在这里使用二等分;跳到中间看看分数是低于还是高于当前界限。然后选择其中的一半,再次测试,直到找到最佳匹配。对分最多需要对数(N)步。Python有一个very fast implementation included;它假定值按递增的顺序递增,因此反转等级和边界:

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

bisect.bisect_right()平分成bounds以找到mark的“插入点”,它将位于列表中相同值的右侧。所以35.0会被插入050.0会被插入3(因为它等于或高于),74.0会被插入5,任何东西都会被插入75.0或更高的6。这些恰好是匹配等级的准确指标。你知道吗

下面是两个Python解决方案。作为一个学习问题,有一些有趣的地方需要理解:带元组键的字典、迭代字典项、生成器表达式、类继承。你知道吗

这不是构建代码的唯一方法。另一种方法是设置边界分数序列,如@MartijnPeter's answer。然而,这些都是可读的、性能合理的解决方案。你知道吗

在这两种情况下,请注意代码中缺少的return语句的重要性。默认情况下,Python返回None。你知道吗

功能性

def grade(mark):

    grds = {(75, 100.1): 'First',
            (70, 75): 'Upper Second',
            (60, 70): 'Second',
            (50, 60): 'Third',
            (45, 50): 'F1 Supp.',
            (40, 45): 'F2',
            (0, 40): 'F3'}

    return next(v for k, v in grds.items() if k[0] <= mark < k[1])

面向对象

python的美妙之处在于它在某种程度上结合了面向对象和函数式编程。考虑以下解决方案。效率与上述类似,但它引入了一个构造,即继承自dict的子类dict_range,可以在其他场景中轻松重用。你知道吗

class dict_range(dict):
    def __getitem__(self, value):
        return next(self.get(k) for k in self.keys() if k[0] <= value < k[1])

def grade(mark):

    grds = dict_range({(75, 100.1): 'First',
                       (70, 75): 'Upper Second',
                       (60, 70): 'Second',
                       (50, 60): 'Third',
                       (45, 50): 'F1 Supp.',
                       (40, 45): 'F2',
                       (0, 40): 'F3'})

    return grds[mark]

两件非常简单的事情:

  • 你从不归还任何东西。默认情况下,Python将返回None。您可以通过在print语句之外或代替print语句添加return语句来解决此问题。你知道吗

    def grade(mark):
        grds = ['First','Upper Second','Second','Third','F1 Supp.','F2','F3']
    
        if mark >= 75.0:
            print("Your grade is",grds[0])
            return grds[0]
        elif 75.0 > mark >= 70.0:
            print("Your grade is",grds[1])
            return grds[1]
    
  • 你可以简化你的表达。Python接受类似于数学范围的表达式范围(例如,0<;=x<;=100是有效的Python)。您可以看到上面的一个例子;我把清理它和使它更可读作为读者的练习。

相关问题 更多 >

    热门问题