编程风格&避免空值

时间:2018-02-06 22:53:17

标签: python python-3.x function for-loop conditional

所以我正在通过Wentworth等人How to Think Like a Computer Scientist开始学习Python 3指南,试图让自己更多地了解编程。虽然它是一个很棒的资源,但它对风格和最佳实践"几乎没有什么可说的。用于在Python 3中编写。

我正在处理有关条件的章节中的一个练习题,这些条件要求我编写一个函数,该函数返回一个字符串' grade'当一个int或float'标记'输入。

我的直接问题是关于函数中条件的重复和函数返回的值。是否有可能以某种方式使用循环使其更简洁,而不是一遍又一遍地编写elif语句?此外,主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())    

5 个答案:

答案 0 :(得分: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()

请注意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]

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

我在这里使用zip() function配对等级名称和边界。您也可以使用enumerate() function生成索引以及每个成绩名称或for index in range(len(grds)):循环,但我发现zip()可以更清晰地工作。

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

但成绩是排序,所以我们可以在这里使用二分;跳到中间,看看标记是低于还是高于当前界限。然后选择一半,再次测试,直到找到最佳匹配。 Bisection最多采用Log(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以找到“插入点”&#39;对于mark,它将是列表中相同值的到右侧。因此,35.00 50.0插入3(因为它等于或更高),74.0位于5 {1}}以及75.0或更高6处的任何内容。而那些恰好是匹配等级的确切指数。

答案 1 :(得分:1)

两件非常简单的事情:

  • 从不返回任何内容。默认情况下,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)。你可以看到上面的一个例子;我将其清理干净,使其更具可读性,作为读者的练习。

答案 2 :(得分:1)

以下是两种pythonic解决方案。作为一个学习问题,有一些有趣的内容需要理解:带有元组键的字典,迭代字典项,生成器表达式,类继承。

这不是构建代码的唯一方式。另一种方法是设置一系列边界分数,如@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_range的构造,子类dict,可以在其他场景中轻松重用。

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]

答案 3 :(得分:0)

首先:为什么你的函数返回None?

因为您实际使用的是print,它会将文本回显给用户。你想要使用的是返回,这将离开函数并基本上说&#34;那是我计算的内容&#34;。

由于没有返回值,python会自动返回None。如果你的语言比较严格,你可能会有错误。

因此,你应该这样做:

return "Your grade is " + grds[0]

第二:如何改进你的代码?

首先要看的是,如果第一个条件有效(mark >= 75.0)那么,在所有的elif中,mark可以更多(或者等于)75,这意味着你可以在这种情况下,摆脱每个elif中的每个低于条件。

第三:如何改进你的代码(2)?

现在,正如我告诉你的那样,返回离开了这个功能。所以你可以用它来删除elifs:

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

    if mark >= 75.0:
        return ("Your grade is " + grds[0])
    if mark >= 70.0:
        return ("Your grade is " + grds[1])
    if mark >= 60.0:
        return ("Your grade is " + grds[2])
    if mark >= 50.0:
        return ("Your grade is " + grds[3])
    if mark >= 45.0:
        return ("Your grade is " + grds[4])
    if mark >= 40.0:
        return ("Your grade is " + grds[5])
    return ("Your grade is " + grds[6])

现在的问题是你有很多次重复相同的代码。这意味着你可以把它分成一个循环。我建议使用这样的边界数组:

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 i in range(len(bounds)):
        if mark >= bounds[i]:
            return ("Your grade is " + grds[i])
    return ("Your grade is " + grds[-1])

我希望我已经清楚地解释了这一点,如果您有任何问题,请在下面问他们。

答案 4 :(得分:0)

与Makoto略有不同的方法。再一次,只是为了一个片段。

if mark >= 75: 
    # limit the religion here by just saving an index
    idx = 0
# you don't need to check its below 75, as that was confirmed by above failing
elif mark >= 70:
    idx = 0
# now consolidate the repetitions here.
return grds[idx]