【问题标题】:Use of return in long if-elseif-else statements (Python)在长 if-elseif-else 语句中使用 return (Python)
【发布时间】:2011-10-11 09:51:15
【问题描述】:

我使用 Python 作为示例,但我的问题是指一般的编程语言。

def some_function(eggs):
    if eggs == 1:
        do_something_1()
    elif eggs == 2:
        do_something_2()
    elif eggs == 3:
        do_something_3()
    else:
        do_error()
        return
    do_something_4()
    do_something_5()
    do_something_6()

(这只是一个例子。我的函数不会被称为do_something_x。)

像这样在 else 中添加 return 是一种不好的编程习惯吗?放一个更好的主意吗

do_something_4()
do_something_5()
do_something_6()

在每个 if/elifs 中?

【问题讨论】:

    标签: python coding-style refactoring if-statement


    【解决方案1】:

    我在您的代码中看到的主要问题是错误案例隐藏在函数体的一半以上。它使代码难以阅读。由于您正在做的是验证函数的参数,因此您应该先这样做。

    在无效参数的情况下,我的偏好是引发适当的异常,例如ValueError。在不知道您的功能做什么或do_error 做什么的情况下,很难绝对肯定地说这适用于您的情况。但一般来说,获得不正确的参数并不是函数可以从中恢复的东西。调用者给出了论点;所以让调用者有责任从错误中恢复。

    另外,您可以使用以下成语来避免elifs 的冗长列表:

    funs = {1: do_something_1,
            2: do_something_2,
            3: do_something_3}
    funs[eggs]()
    

    【讨论】:

    • 甚至funs.get(eggs, do_error)() 来处理eggs 不在funs 中的情况。
    • 验证参数是指if eggs != 1 and eggs != 2 and eggs != 3吗?因为如果我有 100 个鸡蛋的价值选项,那并不是很简单。
    • 如果你使用我建议的成语,那么看安德鲁的评论,或者使用if eggs not in funs: raise ValueError("Invalid number of eggs")
    【解决方案2】:

    绝对不要将相同的代码复制到每个if 子句中。

    怎么样:

    def some_function(eggs):
        options = {1: do_something_1, 2: do_something_2, 3: do_something_3}
        if eggs in options: 
            options[eggs]()
            do_something_4()
            do_something_5()
            do_something_6()
        else:
            do_error()
            return
    

    这不需要很长的ifelifelse。也很清楚do_something_4() 等仅在鸡蛋为 1、2 或 3 时才会发生。

    【讨论】:

      【解决方案3】:

      您确定do_something_n 真的与do_something_m 有关吗?

      如果是这样,请使用 do_something(var, n) 并对带有几个 if 的所有内容使用相同的代码(毕竟这个概念确实相关,对吧?)。

      如果没有,请将函数拆分为真正有用的独立函数。


      举例说明为什么这(可能)不好:

      def print1():
          print(1)
      
      def print2():
          print(2)
      

      嗯,任何人都应该看到这应该是printn(n) 或类似的东西。

      还有另一个概率:

      def action1():
          paymanagers()
          payworkers()
      
      def action2():
          clean_trashbin()
          unlock_car()
      

      这些动作可能不相关,应该属于它们自己的功能。

      【讨论】:

        【解决方案4】:

        您现在所做的编程实践不错,但是通过将三个函数调用放在每个 if 语句中来复制代码是不好的做法。

        有些人更喜欢他们的函数只有一个退出点,在这种情况下,我会建议这样的事情:

        def some_function(eggs):
            error_code = 0
            if eggs == 1:
                do_something_1()
            elif eggs == 2:
                do_something_2()
            elif eggs == 3:
                do_something_3()
            else:
                do_error()
                error_code = 1
        
            if error_code == 0:
                do_something_4()
                do_something_5()
                do_something_6()
            return   # return error_code if it would be helpful to the calling function
        

        【讨论】:

        • 那些人应该使用另一种更实用的语言,如果它对他们来说很重要的话。与使用error_code 风格的变量来修饰代码相比,提前返回更简洁、更易读、更易于维护。
        【解决方案5】:

        怎么样:

        def some_function(eggs):
            if eggs not in [1,2,3]:
                do_error()
                return
        
            if eggs == 1:
                do_something_1()
            elif eggs == 2:
                do_something_2()
            elif eggs == 3:
                do_something_3()
            else:
                assert False
            do_something_4()
            do_something_5()
            do_something_6()
        

        【讨论】:

        • 实际上,我非常喜欢这个答案。有两个阵营:提前返回:“所有函数都应该有一个退出点”阵营和“当你可以限制总处理时返回”阵营。我倾向于支持后者,尤其是 python。它似乎简化了功能,减少了缩进,“感觉”更干净了。
        • 我不太喜欢这里使用的断言,您已经在顶部明确检查了不变量。
        • 我看到的问题是它实际上是代码重复的一个例子——我们想要避免的确切问题。将选项 (1,2,3) 列在多个位置会导致以后出现不匹配的可能性。如果作者添加了选项 4 但没有更新“不在”怎么办?