【问题标题】:What is the intended use of the optional "else" clause of the "try" statement in Python?Python try-else
【发布时间】:2010-10-25 17:46:19
【问题描述】:

try 语句的可选else 子句的预期用途是什么?

【问题讨论】:

  • 大多数答案似乎都集中在为什么我们不能将材料放在 try 子句本身的 else 子句中。问题stackoverflow.com/questions/3996329 专门询问为什么 else 子句代码不能在 try 块本身之后运行,并且该问题被复制到这个问题上,但我在这里没有看到对该问题的明确答复。我觉得stackoverflow.com/a/3996378/1503120 很好地回答了这个问题。我还试图在stackoverflow.com/a/22579805/1503120 阐明各种条款的各种意义。
  • 如果在最终清理之前没有触发异常,那么您希望发生一些事情,这不应该触发相同的异常处理。
  • 在忘记了else 在try/else 和for/else 中的作用无数次之后,我在心理上将它与noexcept 和nobreak 在这些各自的上下文中混为一谈。就我个人而言,我发现它是一个不幸的单词重载,我尽量避免使用它,因为它迫使人们阅读代码想知道“这东西又做了什么?”通常,一个标志、continue 或 break 语句可以用几行额外的代码来传达我想要表达的内容,但肯定更清晰(如果问题的受欢迎程度有任何迹象)。

标签: python exception-handling


【解决方案1】:

如果执行落在try 的底部,则执行else 块中的语句 - 如果没有异常。老实说,我从来没有发现需要。

但是,Handling Exceptions 注意:

使用else子句更好 而不是在尝试中添加额外的代码 子句,因为它避免了意外 捕捉到一个不存在的异常 由受保护的代码引发 try ... except 语句。

因此,如果您有一个方法可以抛出 IOError,并且您想捕获它引发的异常,但如果第一个操作成功,您还需要做其他事情,并且您 不想从那个操作中捕获一个 IOError,你可以这样写:

try:
    operation_that_can_throw_ioerror()
except IOError:
    handle_the_exception_somehow()
else:
    # we don't want to catch the IOError if it's raised
    another_operation_that_can_throw_ioerror()
finally:
    something_we_always_need_to_do()

如果您只是将another_operation_that_can_throw_ioerror() 放在operation_that_can_throw_ioerror 之后,except 将捕获第二个调用的错误。如果你把它放在整个try 块之后,它会一直运行,直到finally 之后。 else 让您确保

  1. 只有在没有异常的情况下才会运行第二个操作,
  2. 它在 finally 块之前运行,并且
  3. 它提出的任何IOErrors 都不会在这里捕获

【讨论】:

  • 另外请记住,try-block 中使用的变量可以在 else-block 中使用,因此如果您不希望 else-block 中出现更多异常,则应始终考虑使用此变体
  • 没关系,因为无论是否有 else,try 作用域的变量都可以在 try 之外看到。
  • 没有“try-scoped variable”这样的东西。在 Python 中,变量作用域仅由模块、函数和推导式建立,而不是控制结构。
  • else 子句允许您编写仅在未引发异常时才有意义的代码; except 子句可以简单地通过。如果将逻辑放在 try 块中,则可能会在代码中默默地隐藏错误。永远不要压制你没有预料到的异常。
  • 从这个答案中不清楚 "falls off the bottom" 是什么意思 - 这不仅是因为异常,还因为 return , continue 或 break.
【解决方案2】:

使用else 有一个重要理由 - 样式和可读性。将可能导致异常的代码保留在处理它们的代码附近通常是一个好主意。例如,比较这些:

try:
    from EasyDialogs import AskPassword
    # 20 other lines
    getpass = AskPassword
except ImportError:
    getpass = default_getpass

和

try:
    from EasyDialogs import AskPassword
except ImportError:
    getpass = default_getpass
else:
    # 20 other lines
    getpass = AskPassword

当except不能提前返回,或者重新抛出异常时,第二个是好的。如果可能的话,我会写:

try:
    from EasyDialogs import AskPassword
except ImportError:
    getpass = default_getpass
    return False  # or throw Exception('something more descriptive')

# 20 other lines
getpass = AskPassword

注意:答案是从最近发布的重复 here 复制而来,因此所有这些“AskPassword”内容。

【讨论】:

    【解决方案3】:

    Python try-else

    try 语句的可选else 子句的预期用途是什么?

    如果没有预期要处理的异常,预期用途是为更多代码运行提供上下文。

    此上下文可避免意外处理您未预料到的错误。

    但是了解导致 else 子句运行的确切条件很重要,因为 return、continue 和 break 可以中断到 else 的控制流。

    总结

    else 语句在no 异常且未被return、continue 或break 语句中断的情况下运行。

    其他答案错过了最后一部分。

    From the docs:

    可选的else 子句在控制从 try 子句的结尾。*

    (加粗。)脚注如下:

    *目前,除了在 异常或执行 return、continue 或 break 语句。

    它确实需要至少一个前面的 except 子句 (see the grammar)。所以它真的不是“try-else”,而是“try-except-else(-finally)”,else(和finally)是可选的。

    Python Tutorial 详细说明了预期用途:

    try ... except 语句有一个可选的 else 子句,当 现在,必须遵循所有 except 子句。它对以下代码很有用 如果 try 子句没有引发异常,则必须执行。为了 示例:

    for arg in sys.argv[1:]:
        try:
            f = open(arg, 'r')
        except IOError:
            print 'cannot open', arg
        else:
            print arg, 'has', len(f.readlines()), 'lines'
            f.close()
    

    else 子句的使用比添加额外的代码要好 try 子句,因为它避免意外捕获异常 不是由受 try 保护的代码引发的……除了 声明。

    区分 else 与 try 块之后的代码的示例

    如果您处理错误,else 块将不会运行。例如:

    def handle_error():
        try:
            raise RuntimeError('oops!')
        except RuntimeError as error:
            print('handled a RuntimeError, no big deal.')
        else:
            print('if this prints, we had no error!') # won't print!
        print('And now we have left the try block!')  # will print!
    

    现在,

    >>> handle_error()
    handled a RuntimeError, no big deal.
    And now we have left the try block!
    

    【讨论】:

    • 来自文档的要点:“使用else 子句比在try 子句中添加额外代码更好,因为它避免了意外捕获代码未引发的异常受到try ... except 语句的保护。”
    【解决方案4】:

    一个用途:测试一些应该引发异常的代码。

    try:
        this_should_raise_TypeError()
    except TypeError:
        pass
    except:
        assert False, "Raised the wrong exception type"
    else:
        assert False, "Didn't raise any exception"
    

    (此代码在实践中应抽象为更通用的测试。)

    【讨论】:

      【解决方案5】:

      Try-except-else 非常适合将the EAFP pattern 与duck-typing 结合使用:

      try:
        cs = x.cleanupSet
      except AttributeError:
        pass
      else:
        for v in cs:
          v.cleanup()
      

      你可能觉得这个幼稚的代码很好:

      try:
        for v in x.cleanupSet:
          v.clenaup()
      except AttributeError:
        pass
      

      这是在代码中意外隐藏严重错误的好方法。我在那里打错了清理,但是让我知道的 AttributeError 被吞下了。更糟糕的是,如果我写得正确,但清理方法偶尔会被传递一个具有错误命名属性的用户类型,导致它在中途默默地失败并留下一个未关闭的文件?祝你调试成功。

      【讨论】:

        【解决方案6】:

        我发现当你有清理工作时它真的很有用,即使有异常也必须完成:

        try:
            data = something_that_can_go_wrong()
        except Exception as e: # yes, I know that's a bad way to do it...
            handle_exception(e)
        else:
            do_stuff(data)
        finally:
            clean_up()
        

        【讨论】:

          【解决方案7】:

          即使你现在想不出它的用途,但你可以打赌它一定是有用的。这是一个缺乏想象力的示例:

          与else:

          a = [1,2,3]
          try:
              something = a[2]
          except:
              print "out of bounds"
          else:
              print something
          

          没有else:

          try:
              something = a[2]
          except:
              print "out of bounds"
          
          if "something" in locals():
              print something
          

          如果没有抛出错误,这里定义了变量something。您可以在 try 块之外删除它,但是如果定义了变量,则需要进行一些混乱的检测。

          【讨论】:

          • try: 块中的something = a[2]; print something 有什么问题?
          • @S.Lott 什么都没有,但是如果有人向您发送了一个列表,并且如果数据不够长,您不想显示数据,因为它可能已损坏,该怎么办?
          • S. Lott:“打印某些东西”可能会引发您不想拦截的不同异常。
          • 我看不出有什么不同。如果我得到一个越界异常,它会打印“越界”。知道了。如果我遇到其他异常,则此代码块不会捕获它。如果我没有例外,那么行为就是打印某物的值,即 a[2]。我看不到 else 在这个例子中做了什么。
          • 'something' 的值在打印时可能会在其 __str__() 方法中引发错误。虽然在此示例中该值实际上只是 2,但您不妨指出这里也没有越界异常。
          【解决方案8】:

          在PEP 380 中有一个很好的try-else 示例。基本上,它归结为在算法的不同部分进行不同的异常处理。

          是这样的:

          try:
              do_init_stuff()
          except:
              handle_init_suff_execption()
          else:
              try:
                  do_middle_stuff()
              except:
                  handle_middle_stuff_exception()
          

          这使您可以在更接近发生异常的位置编写异常处理代码。

          【讨论】:

            【解决方案9】:

            来自Errors and Exceptions # Handling exceptions - docs.python.org

            try ... except 语句有一个可选的else 子句,其中, 当出现时,必须遵循所有 except 子句。它对代码很有用 如果 try 子句没有引发异常,则必须执行。 例如:

            for arg in sys.argv[1:]:
                try:
                    f = open(arg, 'r')
                except IOError:
                    print 'cannot open', arg
                else:
                    print arg, 'has', len(f.readlines()), 'lines'
                    f.close()
            

            else 子句的使用比添加额外的代码要好 try 子句,因为它避免意外捕获异常 不是由受 try 保护的代码引发的……除了 声明。

            【讨论】:

              【解决方案10】:
              try:
                  statements # statements that can raise exceptions
              except:
                  statements # statements that will be executed to handle exceptions
              else:
                  statements # statements that will be executed if there is no exception
              

              例子:

              try:
                  age=int(input('Enter your age: '))
              except:
                  print ('You have entered an invalid value.')
              else:
                  if age <= 21:
                      print('You are not allowed to enter, you are too young.')
                  else:
                      print('Welcome, you are old enough.')
              

              输出:

              >>> 
              Enter your age: a
              You have entered an invalid value.
              >>> RESTART
              >>> 
              Enter your age: 25
              Welcome, you are old enough.
              >>>RESTART
              >>> 
              Enter your age: 13
              You are not allowed to enter, you are too young.
              >>>
              

              复制自:https://geek-university.com/python/the-try-except-else-statements/

              【讨论】:

                【解决方案11】:

                查看Python reference 似乎else 在try 之后执行,当没有异常时。 如果以及当控制从 try 子句的末尾流出时,可选的 else 子句将被执行。 2 else 子句中的异常不被前面的 except 子句处理。

                Dive into python 有一个示例,如果我理解正确,在try 块中,他们尝试导入模块,当失败时,您会得到异常并绑定默认值,但是当它起作用时,您可以选择进入@987654327 @block 并绑定所需的内容(示例和说明请参见链接)。

                如果您尝试在 catch 块中工作,它可能会引发另一个异常 - 我想这就是 else 块派上用场的地方。

                【讨论】:

                • "else 子句中的异常不被前面的 except 子句处理。"那是有用的部分。谢谢。
                • “如果和当控制从 try 子句的末尾流出时执行可选的 else 子句”是另一个区别,因为您可以退出 try 块。
                【解决方案12】:

                就是这样。 try-except 子句的“else”块存在于当(且仅当)尝试操作成功时运行的代码。可以使用,也可以滥用。

                try:
                    fp= open("configuration_file", "rb")
                except EnvironmentError:
                    confdata= '' # it's ok if the file can't be opened
                else:
                    confdata= fp.read()
                    fp.close()
                
                # your code continues here
                # working with (possibly empty) confdata
                

                就个人而言,我喜欢它并在适当的时候使用它。它在语义上对语句进行分组。

                【讨论】:

                  【解决方案13】:

                  我会添加另一个在处理数据库会话时看起来很简单的用例:

                      # getting a DB connection 
                      conn = db.engine.connect()
                  
                      # and binding to a DB session
                      session = db.get_session(bind=conn)
                  
                      try:
                          # we build the query to DB
                          q = session.query(MyTable).filter(MyTable.col1 == 'query_val')
                  
                          # i.e retrieve one row
                          data_set = q.one_or_none()
                  
                          # return results
                          return [{'col1': data_set.col1, 'col2': data_set.col2, ...}]
                  
                      except:
                          # here we make sure to rollback the transaction, 
                          # handy when we update stuff into DB
                          session.rollback()
                          raise
                  
                      else:
                          # when no errors then we can commit DB changes
                          session.commit()
                  
                      finally:
                          # and finally we can close the session
                          session.close()
                  

                  【讨论】:

                  • 对我来说,它似乎只有在你使用 finally 时才有用,否则你可以将 else 代码放在 try 范围之外
                  【解决方案14】:

                  也许用途可能是:

                  #debug = []
                  
                  def debuglog(text, obj=None):
                      " Simple little logger. "
                      try:
                          debug   # does global exist?
                      except NameError:
                          pass    # if not, don't even bother displaying
                      except:
                          print('Unknown cause. Debug debuglog().')
                      else:
                          # debug does exist.
                          # Now test if you want to log this debug message
                          # from caller "obj"
                          try:
                              if obj in debug:
                                  print(text)     # stdout
                          except TypeError:
                              print('The global "debug" flag should be an iterable.')
                          except:
                              print('Unknown cause. Debug debuglog().')
                  
                  def myfunc():
                      debuglog('Made it to myfunc()', myfunc)
                  
                  debug = [myfunc,]
                  myfunc()
                  

                  也许这会让你也用得上。

                  【讨论】:

                    【解决方案15】:

                    我发现try: ... else: 构造在您运行数据库查询并将这些查询的结果记录到相同风格/类型的单独数据库的情况下很有用。假设我有很多工作线程都处理提交到队列的数据库查询

                    #in a long running loop
                    try:
                        query = queue.get()
                        conn = connect_to_db(<main db>)
                        curs = conn.cursor()
                        try:
                            curs.execute("<some query on user input that may fail even if sanitized">)
                        except DBError:
                            logconn = connect_to_db(<logging db>)
                            logcurs = logconn.cursor()
                            logcurs.execute("<update in DB log with record of failed query")
                            logcurs.close()
                            logconn.close()
                        else:
                    
                            #we can't put this in main try block because an error connecting
                            #to the logging DB would be indistinguishable from an error in 
                            #the mainquery 
                    
                            #We can't put this after the whole try: except: finally: block
                            #because then we don't know if the query was successful or not
                    
                            logconn = connect_to_db(<logging db>)
                            logcurs = logconn.cursor()
                            logcurs.execute("<update in DB log with record of successful query")
                            logcurs.close()
                            logconn.close()
                            #do something in response to successful query
                    except DBError:
                        #This DBError is because of a problem with the logging database, but 
                        #we can't let that crash the whole thread over what might be a
                        #temporary network glitch
                    finally:
                        curs.close()
                        conn.close()
                        #other cleanup if necessary like telling the queue the task is finished
                    

                    当然,如果您可以区分可能引发的异常,则不必使用它,但是如果对成功的代码段做出反应的代码可能会引发与成功的代码段相同的异常,那么您可以'不要只是让第二个可能的异常消失,或者在成功时立即返回(在我的情况下这会杀死线程),那么这确实派上用场了。

                    【讨论】:

                      【解决方案16】:

                      这是我喜欢使用这种模式的另一个地方:

                       while data in items:
                           try
                              data = json.loads(data)
                           except ValueError as e:
                              log error
                           else:
                              # work on the `data`
                      

                      【讨论】:

                      • 您可以改用continue——“早点爆发”模式。这允许您删除“else”子句及其缩进,使代码更易于阅读。
                      【解决方案17】:

                      else 块通常可以用来补充每个 except 块中出现的功能。

                      try:
                          test_consistency(valuable_data)
                      except Except1:
                          inconsistency_type = 1
                      except Except2:
                          inconsistency_type = 2
                      except:
                          # Something else is wrong
                          raise
                      else:
                          inconsistency_type = 0
                      
                      """
                      Process each individual inconsistency down here instead of
                      inside the except blocks. Use 0 to mean no inconsistency.
                      """
                      

                      在这种情况下,inconsistency_type 被设置在每个 except 块中,因此在else 的无错误情况下,该行为得到了补充。

                      当然,我将其描述为有一天可能会出现在您自己的代码中的一种模式。在这种特定情况下,您只需在 try 块之前将 inconsistency_type 设置为 0。

                      【讨论】:

                        【解决方案18】:

                        我能想到的使用场景之一是不可预知的异常,如果你再试一次就可以规避。例如,当 try 块中的操作涉及随机数时:

                        while True:
                            try:
                                r = random.random()
                                some_operation_that_fails_for_specific_r(r)
                            except Exception:
                                continue
                            else:
                                break
                        

                        但是,如果可以预测异常,则应始终事先选择验证而不是异常。然而,并不是所有的事情都可以预测,所以这个 Code Pattern 有它的位置。

                        【讨论】:

                        • 您可以将break 放在最后的try 中,这样IMO 更干净,您不需要else。 continue 也不是真的需要,你可以只需要pass。
                        【解决方案19】:

                        我发现else 可用于处理可能不正确的配置文件:

                        try:
                            value, unit = cfg['lock'].split()
                        except ValueError:
                            msg = 'lock monitoring config must consist of two words separated by white space'
                            self.log('warn', msg)
                        else:
                             # get on with lock monitoring if config is ok
                        

                        读取 lock 配置的异常会禁用锁定监控,并且 ValueErrors 会记录有用的警告消息。

                        【讨论】:

                          【解决方案20】:

                          假设您的编程逻辑取决于字典是否具有具有给定键的条目。您可以使用if... else... 构造测试dict.get(key) 的结果,也可以这样做:

                          try:
                              val = dic[key]
                          except KeyError:
                              do_some_stuff()
                          else:
                              do_some_stuff_with_val(val)
                          

                          【讨论】:

                            【解决方案21】:

                            else: 块令人困惑且(几乎)无用。它也是for 和while 语句的一部分。

                            实际上,即使是在 if 声明中,else: 也可能以非常糟糕的方式被滥用,从而产生很难找到的错误。

                            考虑一下。

                               if a < 10:
                                   # condition stated explicitly
                               elif a > 10 and b < 10:
                                   # condition confusing but at least explicit
                               else:
                                   # Exactly what is true here?
                                   # Can be hard to reason out what condition is true
                            

                            三思而后行else:。这通常是一个问题。避免它,除非在 if-statement 中,甚至考虑记录 else- 条件以使其明确。

                            【讨论】:

                            • 我不同意这个。在“if-elif”块中,“else”被用作“默认”将用于C语言的“case”块中。始终建议处理“默认”情况,即使您认为自己已经涵盖了各种情况下的所有情况。
                            • @Josip:用作“默认值”可能会造成混淆。问题是要清楚地定义这个“默认”的条件。定义不明确的默认条件可能是错误行为的根本原因。否则可能会造成混乱。在所有情况下都应该仔细考虑,不仅仅是尝试,暂时,但如果也是。
                            • 好吧,上面的代码完全是抽象的,没有做任何有意义的事情,所以是的——难怪它会让人困惑。
                            • @S.Lott “这会减少错误” - 我的观点是这是错误的。我认为我们只是在意见上存在真正的分歧。糟糕的程序员总能找到编写错误程序的方法。总是。优秀的程序员总是寻求好的实践,并且可以用几乎任何语言编写好的代码。消除有用的结构只会减少优秀程序员的能力,而不会特别帮助坏程序员,因为他们能够发明无数种方法来搞砸事情。
                            • 考虑:if x &gt; 0: return "yes" 和 if x &lt;= 0: return "no"。现在有人来了,将其中一个条件更改为x &gt; 1,但忘记更改另一个条件。这如何减少将提交的错误数量。 if else 子句有时相隔多行。 DRY 是一种很好的做法,实际上很多时候。 (抱歉重复发帖)。
                            猜你喜欢
                            • 2021-03-15
                            • 2016-08-30
                            • 2011-01-23
                            • 1970-01-01
                            • 1970-01-01
                            • 2011-06-19
                            • 1970-01-01
                            • 1970-01-01
                            • 2020-08-19
                            相关资源
                            最近更新 更多