【问题标题】:Using 'try' vs. 'if' in Python在python中使用try vs if
【发布时间】:2010-12-22 14:06:03
【问题描述】:

在测试变量是否有值时,是否有理由决定使用tryif 构造中的哪一个?

例如,有一个函数要么返回一个列表,要么不返回一个值。我想在处理之前检查结果。以下哪一项更可取,为什么?

result = function();
if (result):
    for r in result:
        #process items

result = function();
try:
    for r in result:
        #process items
except TypeError:
    pass;

相关讨论:

Checking for member existence in Python

【问题讨论】:

  • 请记住,如果您的#process items 节可能会抛出 TypeError,您需要在另一个 try: except: 块中包装。对于这个特定的例子,我只使用 if:

标签: python


【解决方案1】:

您的第二个示例已损坏 - 代码永远不会抛出 TypeError 异常,因为您可以遍历字符串和列表。遍历空字符串或列表也是有效的——它将执行循环体零次。

【讨论】:

    【解决方案2】:

    一般来说,我得到的印象是,应该为特殊情况保留例外。如果 result 预计永远不会为空(但可能是,例如,如果磁盘崩溃等),则第二种方法是有意义的。另一方面,如果空的result 在正常情况下是完全合理的,那么使用if 语句对其进行测试更有意义。

    我想到了(更常见的)场景:

    # keep access counts for different files
    file_counts={}
    ...
    # got a filename somehow
    if filename not in file_counts:
        file_counts[filename]=0
    file_counts[filename]+=1
    

    而不是等效的:

    ...
    try:
        file_counts[filename]+=1
    except KeyError:
        file_counts[filename]=1
    

    【讨论】:

    • 这是 Tim Pietzcker 提到的方法之间差异的一个示例:第一个是 LBYL,第二个是 EAFP
    • 请注意++在python中不起作用,请改用+= 1
    【解决方案3】:

    你经常听到 Python 鼓励 EAFP 风格(“请求宽恕比请求许可更容易”)而不是 LBYL 风格(“先看再跳跃”)。对我来说,这是效率和可读性的问题。

    在您的示例中(假设该函数不是返回列表或空字符串,而是返回列表或None),如果您预计 99% 的时间 result 实际上会包含可迭代的内容,我会使用try/except 方法。如果异常确实异常,它会更快。如果resultNone 超过50% 的时间,那么使用if 可能会更好。

    通过一些测量来支持这一点:

    >>> import timeit
    >>> timeit.timeit(setup="a=1;b=1", stmt="a/b") # no error checking
    0.06379691968322732
    >>> timeit.timeit(setup="a=1;b=1", stmt="try:\n a/b\nexcept ZeroDivisionError:\n pass")
    0.0829463709378615
    >>> timeit.timeit(setup="a=1;b=0", stmt="try:\n a/b\nexcept ZeroDivisionError:\n pass")
    0.5070195056614466
    >>> timeit.timeit(setup="a=1;b=1", stmt="if b!=0:\n a/b")
    0.11940114974277094
    >>> timeit.timeit(setup="a=1;b=0", stmt="if b!=0:\n a/b")
    0.051202772912802175
    

    因此,虽然if 声明总是会花费您,但设置try/except 块几乎是免费的。但是当Exception真正发生时,成本要高得多。

    道德:

    • 使用try/except 进行流量控制是完全可以的(和“pythonic”),
    • 但当Exceptions 实际上是例外时,它最有意义。

    来自 Python 文档:

    EAFP

    请求宽恕比请求宽恕更容易 允许。这种常见的 Python 编码 样式假定存在有效 键或属性和捕获 假设证明的例外情况 错误的。这种干净快速的风格是 特点是存在许多 tryexcept 语句。这 技术与LBYL形成对比 许多其他语言共有的风格 比如C。

    【讨论】:

    • 谢谢。我看到在这种情况下,基本原理可能是对结果的期望。
    • .... 这就是为什么很难为 Python 进行真正的优化 JIT 的(众多)原因之一。正如最近测试版 LuaJIT 2 所证明的那样,动态语言可以非常非常快;但这在很大程度上取决于最初的语言设计和它所鼓励的风格。 (在相关的说明中,语言设计是为什么即使是最好的 JavaScirpt JIT 也无法与 LuaJIT 1 相比,更不用说 2)
    • @2rs2ts:我自己也做了类似的计时。在 Python 3 中,对于键在字典中的情况,try/exceptif key in d: 快 25%。当密钥不在字典中时,速度要慢得多,正如预期的那样,并且与这个答案一致。
    • 我知道这是一个旧答案,但是:使用诸如 1/1 之类的语句与 timeit 不是一个好选择,因为它们将被优化(参见 dis.dis('1/1') 并注意不会发生除法) .
    • 这也取决于您愿意执行的操作。单个除法速度很快,捕捉错误有点昂贵。但即使在异常处理成本可以接受的情况下,也要仔细考虑您要求计算机做什么。如果无论结果如何,操作本身都非常昂贵,并且有一种便宜的方法可以判断是否可能成功:使用它。示例:不要为了发现空间不足而复制文件的一半。
    【解决方案4】:

    您的函数不应返回混合类型(即列表或空字符串)。它应该返回一个值列表或只是一个空列表。然后你不需要测试任何东西,即你的代码折叠到:

    for r in function():
        # process items
    

    【讨论】:

    • 我完全同意你的观点;但是,这个功能不是我的,我只是在使用它。
    • @artdanil:所以你可以将这个函数封装在你编写的函数中,就像 Brandon Corfman 正在考虑的那样工作。
    • 这引出了一个问题:包装器应该使用 if 还是 try 来处理不可迭代的情况?
    • @quamrana 这正是我想要做的。正如@jcd 指出的那样,问题是我应该如何处理包装函数中的情况。
    【解决方案5】:

    作为一般经验法则,您应该永远使用 try/catch 或任何异常处理东西来控制流程。即使通过引发StopIteration 异常来控制幕后迭代,您仍然应该更喜欢您的第一个代码 sn-p 而不是第二个。

    【讨论】:

    • 这在 Java 中是正确的。 Python 非常重视 EAFP。
    • 这仍然是一种奇怪的做法。根据我的经验,大多数程序员的大脑都会跳过 EAFP。他们最终想知道为什么选择了某个路径。话虽如此,打破每条规则都有时间和地点。
    【解决方案6】:

    如果我提供的代码乍一看不明显,请忽略我的解决方案,您必须阅读代码示例后的说明。

    我可以假设“无返回值”意味着返回值为无吗?如果是,或者如果“无值”是 False 布尔值,您可以执行以下操作,因为您的代码本质上将“无值”视为“不迭代”:

    for r in function() or ():
        # process items
    

    如果function() 返回不正确的内容,则迭代空元组,即不运行任何迭代。这本质上是 LBYL。

    【讨论】:

      【解决方案7】:

      以下哪个更可取,为什么?

      在这种情况下,“三思而后行”更为可取。使用异常方法,TypeError 可能发生在循环体中的任何位置,它会被捕获并丢弃,这不是您想要的,并且会使调试变得棘手。

      (不过,我同意 Brandon Corfman 的观点:为“无项目”返回 None 而不是一个空列表是错误的。这是 Java 编码人员的一个不愉快的习惯,不应该在 Python 或 Java 中看到。)

      【讨论】:

        【解决方案8】:

        就性能而言,对通常的代码使用 try 块 不引发异常比每次都使用 if 语句要快。因此,决定取决于特殊情况的概率。

        【讨论】:

          【解决方案9】:

          bobince 明智地指出,包装第二种情况也可以在循环中捕获 TypeErrors,这不是您想要的。如果你真的想尝试一下,你可以在循环之前测试它是否可迭代

          result = function();
          try:
              it = iter(result)
          except TypeError:
              pass
          else:
              for r in it:
                  #process items
          

          如您所见,它相当丑陋。我不建议这样做,但为了完整起见,应该提及它。

          【讨论】:

          • 在我看来,故意遇到类型错误总是不好的编码风格。开发人员应该知道会发生什么,并准备好毫无例外地处理这些值。每当一个操作做了它应该做的事情(从程序员的角度来看)并且预期的结果是一个列表或None,总是用is Noneis not None检查结果。另一方面,为合法结果引发异常也是不好的风格。意外情况除外。示例:str.find() 如果未找到任何内容,则返回 -1,因为搜索本身已完成且没有错误。
          猜你喜欢
          • 2011-04-25
          • 2016-08-30
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2015-09-10
          • 1970-01-01
          • 2020-02-25
          • 2017-06-08
          相关资源
          最近更新 更多