【问题标题】:It is more efficient to use if-return-return or if-else-return?使用 if-return-return 还是 if-else-return 效率更高?
【发布时间】:2012-02-29 18:34:47
【问题描述】:

假设我有一个带有returnif 语句。从效率的角度来看,我应该使用

if(A > B):
    return A+1
return A-1

if(A > B):
    return A+1
else:
    return A-1

在使用编译语言 (C) 还是脚本语言 (Python) 时,我应该更喜欢一种还是另一种?

【问题讨论】:

  • 在编译语言中,您无需过多担心效率。编译器对此进行了排序。您应该编写代码以便阅读。 (您仍然需要担心算法的效率,并且草率地使用类型等会影响效率 - 您只是不必过多担心自己的风格。)不过我不了解 Python。
  • 依靠你的编译器来整理你的代码是一个危险的步骤——并且需要一个可靠的编译器。如果你知道你想让你的代码做什么就更好了!
  • 如果您所做的事情是由规范定义的,那么我认为没有任何理由怀疑编译器。它会写成比你聪明得多的人,而且你犯错的可能性比他们大得多。
  • 如何根据意见关闭? 您知道两者之间没有性能差异之后可能是一种意见。我没有,而且我很确定很多人也没有。
  • 虽然这个问题很受欢迎,但如果没有考虑到特定的语言,就无法准确回答,否则,对于这种格式来说,回答每种语言都会太长。

标签: python c performance compiler-construction


【解决方案1】:

由于return 语句终止了当前函数的执行,这两种形式是等价的(尽管第二种形式可以说比第一种更具可读性)。

两种形式的效率是相当的,如果if条件为假,底层机器代码必须执行跳转。

请注意,Python 支持的语法允许您在您的情况下仅使用一个 return 语句:

return A+1 if A > B else A-1

【讨论】:

  • C 也支持这一点。 return (A>B)?A+1:A-1; 但是,编写这样的代码绝对没有性能提升。我们所做的只是使代码变得模糊、不可读,并且在某些情况下更容易受到隐式类型提升的影响。
  • @Lundin 被混淆了?不可读?仅适用于不了解三元运算符的人。
  • @Lundin 根据这个论证,< 是不好的做法,因为-1 < 1u 会产生意想不到的结果。
  • @glglgl:不,因为人们希望 ?: 运算符表现得像 if-else,但事实并非如此。如果有人会编写像-1 < 1u 这样的代码,我对此表示怀疑,他们会很容易发现这个错误。然而,相当多的人会编写我发布的代码的某个版本。我在生产代码中经常看到这样的错误,以至于无法信任 ?: 运算符。同样作为一个经验法则,如果语言给你两种不同的方式来做同样的事情,只使用其中一种,不要根据你的心情随意选择两种。
  • @Lundin 这是在 C 中小心使用 ?: 的一个论点,但您似乎说它也适用于 Python。您能否指出在 Python 中使用三元组会导致意外结果的任何示例?
【解决方案2】:

来自Chromium's风格指南:

return 后不要使用 else:

# Bad
if (foo)
  return 1
else
  return 2

# Good
if (foo)
  return 1
return 2

return 1 if foo else 2

【讨论】:

  • 谢谢。 +1。请问return后为什么不用else?
  • 我很惊讶,因为第一个看起来更清晰,因此更好。
  • 你可以为任何一个提出合理的理由。这个决定 IMO 中最重要的事情是在代码库中保持一致。
  • 在大多数情况下,您可能会发现 if-else-return 分支几乎永远不会相等(如果它们相等,那么您无论如何都应该重构;使用 switch 构造或 Python,枚举字典/使用可调用/等)。因此,几乎所有if-else-return 都是保护子句的情况,并且在没有else 的情况下始终是可测试的(模拟测试的表达式)。
  • 我不买。阅读一个单词“其他”比在脑海中弄清楚发生了什么要高效得多。即使很明显。一个词:“其他”总是会更明显。心理学 101
【解决方案3】:

我个人尽可能避免使用else 块。见Anti-if Campaign

此外,他们不会为线路收取“额外费用”,你知道的:p

"Simple is better than complex" & “可读性为王”

delta = 1 if (A > B) else -1
return A + delta

【讨论】:

  • 为什么投反对票?是一个“pythonic”的答案。您可能不认为它是首选答案。但不是无效的。我也遵循KISS原则en.wikipedia.org/wiki/KISS_principle
  • 我赞成你的回答,因为对我来说它在可读性和简单性方面得分。我个人认为,如果有人在没有告诉我为什么我的回答是积极否定的情况下就对我投了反对票,这是令人反感的。
  • 以前没有听说过 Anti-if 活动,但可以理解为什么 if 会很危险。我总是尝试限制 if 语句所包含的代码数量,并尝试重写 elif 树以使用 dict。不过这有点离题了。
  • @StephenEllwood 使用dicts 来避免差异在性能方面是一个非常糟糕的主意。
  • @Bachsau 你可能是对的。我从来不用担心性能,因为我所有的脚本都在几秒钟内运行。对我来说,可读性通常胜过性能。因为我不是全职程序员;它们只是达到目的的一种手段。
【解决方案4】:

对于任何明智的编译器,您都应该观察到没有区别;它们应该被编译成相同的机器码,因为它们是等价的。

【讨论】:

    【解决方案5】:

    关于编码风格:

    无论哪种语言,大多数编码标准都禁止单个函数使用多个 return 语句,这是一种不好的做法。

    (虽然我个人认为在某些情况下多个返回语句确实有意义:文本/数据协议解析器、具有大量错误处理的函数等)

    所有这些行业编码标准的共识是表达式应该写成:

    int result;
    
    if(A > B)
    {
      result = A+1;
    }
    else
    {
      result = A-1;
    }
    return result;
    

    关于效率:

    上面的例子和问题中的两个例子在效率上都是完全等价的。在所有这些情况下,机器代码都必须比较 A > B,然后跳转到 A+1 或 A-1 计算,然后将结果存储在 CPU 寄存器或堆栈中。

    编辑:

    来源:

    • MISRA-C:2004 规则 14.7,该规则又引用了...:
    • IEC 61508-3。第 3 部分,表 B.9。
    • IEC 61508-7。 C.2.9.

    【讨论】:

    • 您确定单返回宗教已经感染了大多数编码标准吗?那会很可怕。
    • 我会说这个规则在大多数情况下都没有意义。我倾向于发现代码更易读,更容易理解,并在适当的时候返回。但这只是我。但是,我想到的是每个公司/项目的编码标准,而不是像 MISRA 这样的东西,否则愚蠢的处方有时可能会有一些优点。我希望大多数人没有接受单一出口点的想法。
    • @DanielFischer:在我为我公司设计的基于 MISRA 的 C 编码标准中,我有一条规则“一个函数只能有一个退出点,在函数的末尾,除非单点退出会降低代码的可读性”。所以它是 MISRA-C,但有一个例外。如果你编写一个高级解析器函数,它可以返回 10 个不同的错误,嵌套大括号的级别会使代码完全不可读 - 在这种情况下,遇到错误时立即返回会更明智。
    • 请参阅this SO question 以了解有关单出口问题的讨论和进一步讨论的进一步链接。除了过时且过于“工程化”的单出口规则之外,Python 特别 提升了"flat is better than nested" view,并将return 无论在哪里 clear 是 Python 的惯用方式。
    • @percebus 我完全同意,圈复杂度是反对单一回报的一个很好的论据。我已经多次向 MISRA 委员会询问过这个问题,例如see this。至少该规则在 MISRA-C:2012 中被降级为建议。
    【解决方案6】:

    版本 A 更简单,这就是我使用它的原因。

    如果您在 Java 中打开所有编译器警告,您将在第二个版本上收到警告,因为它是不必要的并且会增加代码复杂性。

    【讨论】:

      【解决方案7】:

      这是一个风格(或偏好)的问题,因为口译员并不关心。就我个人而言,我会尽量不做一个函数的最终声明,它返回一个缩进级别的值而不是函数基。示例 1 中的 else 稍微模糊了函数的结束位置。

      根据我的喜好:

      return A+1 if (A > B) else A-1
      

      因为它既遵守将单个 return 语句作为函数中的最后一条语句的良好约定(如前所述),又遵守避免命令式中间结果的良好函数式编程范式。

      对于更复杂的函数,我更喜欢将函数分解为多个子函数,以避免可能的过早返回。否则,我将恢复使用名为 rval 的命令式样式变量。我尽量不要使用多个 return 语句,除非函数是微不足道的,或者 end 之前的 return 语句是由于错误导致的。过早返回凸显了你无法继续前进的事实。对于设计为分支成多个子函数的复杂函数,我尝试将它们编码为 case 语句(例如由 dict 驱动)。

      一些海报提到了运行速度。运行速度对我来说是次要的,因为如果你需要执行速度,Python 并不是最好的语言。我使用 Python 作为对我很重要的编码效率(即编写无错误代码)。

      【讨论】:

      • 如果用户要否决我的回答,我会很感激他们为什么认为我错了的评论。
      • 为了便于阅读,我可能会在每条语句前只写一行。 var n = 1 if (A > B) else -1return A+n
      • @percebus 在某些情况下,如果变量名可以增强含义,我会同意。例如:'code' move_x = 1 if my_x
      • 顺便说一句,我实际上赞成你的回答。如果你看到我的答案很相似
      【解决方案8】:

      我知道这个问题被标记为 python,但它提到了动态语言,所以我想我应该提一下,在 ruby​​ 中,if 语句实际上有一个返回类型,所以你可以做类似的事情

      def foo
        rv = if (A > B)
               A+1
             else
               A-1
             end
        return rv 
      end
      

      或者因为它也有简单的隐式返回

      def foo 
        if (A>B)
          A+1
        else 
          A-1
        end
      end
      

      很好地解决了没有多次退货的风格问题。

      【讨论】:

        【解决方案9】:

        从性能的角度来看,这在 Python 中并不重要,而且我假设所有现代语言都一样。

        这真的归结为样式和可读性。您的第二个选项(if-else 块)比第一个更具可读性,并且比单线三元操作更具可读性。

        【讨论】:

          猜你喜欢
          • 2015-10-20
          • 2012-03-05
          • 2017-02-05
          • 1970-01-01
          • 2017-05-16
          • 2015-10-28
          • 2012-01-10
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多