【问题标题】:Why do we need the "finally" clause in Python?为什么我们需要 Python 中的“finally”子句?
【发布时间】:2012-07-18 02:46:27
【问题描述】:

我不确定为什么我们在 try...except...finally 语句中需要 finally。在我看来,这个代码块

try:
    run_code1()
except TypeError:
    run_code2()
other_code()

使用finally与这个相同:

try:
    run_code1()
except TypeError:
    run_code2()
finally:
    other_code()

我错过了什么吗?

【问题讨论】:

    标签: python exception-handling try-finally


    【解决方案1】:

    如果你早点回来会有所不同:

    try:
        run_code1()
    except TypeError:
        run_code2()
        return None   # The finally block is run before the method returns
    finally:
        other_code()
    

    比较一下:

    try:
        run_code1()
    except TypeError:
        run_code2()
        return None   
    
    other_code()  # This doesn't get run if there's an exception.
    

    其他可能导致差异的情况:

    • 如果在 except 块内引发异常。
    • 如果在run_code1() 中抛出异常但不是TypeError
    • 其他控制流语句,例如continuebreak 语句。

    【讨论】:

    • try: #x = Hello + 20 x = 10 + 20 except: print '我在除了块' x = 20 + 30 else: print '我在其他块' x += 1最后:打印'最后 x = %s' %(x)
    【解决方案2】:

    您可以使用finally 确保文件或资源被关闭或释放,无论是否发生异常,即使您没有捕获异常。(或者如果您没有捕获那个特定的异常。)

    myfile = open("test.txt", "w")
    
    try:
        myfile.write("the Answer is: ")
        myfile.write(42)   # raises TypeError, which will be propagated to caller
    finally:
        myfile.close()     # will be executed before TypeError is propagated
    

    在这个例子中你最好使用with 语句,但是这种结构可以用于其他类型的资源。

    几年后,我写了a blog post 关于finally 的滥用,读者可能会觉得很有趣。

    【讨论】:

      【解决方案3】:

      它们不等价。 finally 无论发生什么其他情况,代码都会运行*。 它对于清理必须运行的代码很有用。


      *: 正如Mark Byers 评论的那样,任何导致进程立即终止的事情也会阻止finally 代码运行。 后者可能是os._exit(). 或断电,但无限循环或其他东西也属于该类别。

      【讨论】:

        【解决方案4】:

        为了补充上述其他答案,finally 子句无论如何都会执行,而 else 子句仅在未引发异常时才会执行。

        例如,写入一个没有异常的文件将输出以下内容:

        file = open('test.txt', 'w')
        
        try:
            file.write("Testing.")
            print("Writing to file.")
        except IOError:
            print("Could not write to file.")
        else:
            print("Write successful.")
        finally:
            file.close()
            print("File closed.")
        

        输出:

        Writing to file.
        Write successful.
        File closed.
        

        如果出现异常,代码会输出以下内容,(注意,保持文件只读会导致故意错误。

        file = open('test.txt', 'r')
        
        try:
            file.write("Testing.")
            print("Writing to file.")
        except IOError:
            print("Could not write to file.")
        else:
            print("Write successful.")
        finally:
            file.close()
            print("File closed.")
        

        输出:

        Could not write to file.
        File closed.
        

        我们可以看到finally 子句无论是否有异常都会执行。希望这会有所帮助。

        【讨论】:

        • 即使您没有使用无法回答问题的“finally”子句,这也会起作用,因为 OP 想知道差异,一个很好的例子会导致不同error 比 IOError,表明 finally 子句块在异常传播到调用者之前执行。
        • 我不知道else 是一个东西。了解有用。
        【解决方案5】:

        documentation 中所述,finally 子句旨在定义必须在所有情况下执行的清理操作。

        如果存在finally,它指定一个“清理”处理程序。 try 子句被执行,包括任何exceptelse 子句。如果 异常发生在任何子句中且未处理,则 暂时保存异常。 finally 子句被执行。如果 有一个保存的异常,它在finally 的末尾重新引发 条款。

        一个例子:

        >>> def divide(x, y):
        ...     try:
        ...         result = x / y
        ...     except ZeroDivisionError:
        ...         print("division by zero!")
        ...     else:
        ...         print("result is", result)
        ...     finally:
        ...         print("executing finally clause")
        ...
        >>> divide(2, 1)
        result is 2.0
        executing finally clause
        >>> divide(2, 0)
        division by zero!
        executing finally clause
        >>> divide("2", "1")
        executing finally clause
        Traceback (most recent call last):
          File "<stdin>", line 1, in <module>
          File "<stdin>", line 3, in divide
        TypeError: unsupported operand type(s) for /: 'str' and 'str'
        

        如您所见,finally 子句无论如何都会执行。通过划分两个字符串引发的TypeError 不由except 子句处理,因此在执行finally 子句后重新引发。

        在实际应用中,finally 子句对于释放外部资源(如文件或网络连接)很有用,无论资源的使用是否成功。

        【讨论】:

          【解决方案6】:

          代码块不等价。如果run_code1() 抛出TypeError 以外的异常,或者run_code2() 抛出异常,finally 子句也将运行,而第一个版本中的other_code() 在这些情况下不会运行。

          【讨论】:

            【解决方案7】:

            在您的第一个示例中,如果run_code1() 引发不是TypeError 的异常会发生什么? ... other_code() 不会被执行。

            finally: 版本相比:other_code() 保证执行,无论引发任何异常。

            【讨论】:

              【解决方案8】:

              多年来,专业地使用 delphi 教会了我使用 finally 来保护我的清理例程。 Delphi 几乎强制使用 finally 来清理在 try 块之前创建的任何资源,以免导致内存泄漏。这也是 Java、Python 和 Ruby 的工作方式。

              resource = create_resource
              try:
                use resource
              finally:
                resource.cleanup
              

              无论你在 try 和 finally 之间做什么,资源都会被清理。此外,如果执行从未到达try 块,它也不会被清理。 (即create_resource 本身会引发异常)它使您的代码“异常安全”。

              至于为什么您实际上需要 finally 块,并非所有语言都这样做。在 C++ 中,您自动调用析构函数,当异常展开堆栈时强制清理。我认为与 try...finally 语言相比,这是朝着更简洁的代码方向迈出的一步。

              {    
                type object1;
                smart_pointer<type> object1(new type());
              } // destructors are automagically called here in LIFO order so no finally required.
              

              【讨论】:

                【解决方案9】:

                这里有一段代码来说明区别:

                ...
                
                try: 
                  a/b
                  print('In try block')
                  
                except TypeError:
                  print('In except block')
                  
                finally: 
                  print('In finally block')
                
                print('Outside')
                

                a, b = 0, 1

                输出:

                In try block 
                In finally block 
                Outside
                

                (没有错误,除了跳过块。)


                a, b = 1, 0

                输出:

                In finally block
                
                Traceback (most recent call last):
                a/b
                ZeroDivisionError: division by zero
                

                (没有为 ZeroDivisionError 指定异常处理,只执行 finally 块。


                a, b = 0, '1'

                输出:

                In except block 
                In finally block 
                Outside
                

                (异常处理妥当,程序不中断。)


                注意:如果您有一个 except 块来处理所有类型的错误,那么 finally 块将是多余的。

                【讨论】:

                  【解决方案10】:

                  最后,当您想在运行主要工作的代码之前运行“可选”代码并且该可选代码可能由于各种原因而失败时,也可以使用。

                  在下面的例子中,我们并不确切知道store_some_debug_info 可能会抛出什么样的异常。

                  我们可以跑:

                  try:
                    store_some_debug_info()
                  except Exception:
                    pass
                  do_something_really_important() 
                  

                  但是,大多数 linter 都会抱怨捕获的异常太模糊。此外,由于我们选择仅pass 处理错误,except 块并没有真正增加价值。

                  try:
                    store_some_debug_info()
                  finally:
                    do_something_really_important()     
                  

                  上面的代码和第一块代码的效果一样,但是更简洁。

                  【讨论】:

                    【解决方案11】:

                    finally 用于定义“清理操作”finally 子句在离开try 语句之前的任何情况下都会执行,无论是否发生异常(即使您不处理它)。

                    我第二个@Byers 的例子。

                    【讨论】:

                      【解决方案12】:

                      完美的例子如下:

                      try:
                          #x = Hello + 20
                          x = 10 + 20 
                      except:
                          print 'I am in except block'
                          x = 20 + 30
                      else:
                          print 'I am in else block'
                          x += 1
                      finally:
                          print 'Finally x = %s' %(x)
                      

                      【讨论】:

                        【解决方案13】:

                        try 块只有一个强制子句:try 语句。 except、else 和 finally 子句是可选的,并且基于用户偏好。

                        最后: 在 Python 离开 try 语句之前,它会在任何条件下运行 finally 块中的代码,即使它正在结束程序。例如,如果 Python 在运行 except 或 else 块中的代码时遇到错误,finally 块仍将在停止程序之前执行。

                        【讨论】:

                        • 这是错误的。 except 语句是强制性的。 – Lucas Azevedo 2 月 1 日 12:04 这是错误的,因为我刚刚编译并运行了一个带有 try-finally 块的 Python 3.5 程序,没有“except”子句。
                        • 我自己试过了,但令我难以置信的是,except 子句不是强制性的。
                        【解决方案14】:

                        尝试在没有 finally 块的情况下先运行此代码,

                        1 / 0 导致除以零错误。

                            try:
                                1 / 0    
                                print(1)
                                
                            except Exception as e:
                                1 / 0
                                print(e)
                                
                        

                        然后尝试运行这段代码,

                            try:
                                1 / 0    
                                print(1)
                                
                            except Exception as e:
                                1 / 0
                                print(e)
                           
                            finally:
                                print('finally')
                        

                        对于第一种情况,您没有 finally 块,
                        因此,当 except 块中发生错误时,程序执行将停止,并且您无法在 except 块之后执行任何操作。

                        但是对于第二种情况,
                        错误发生但在程序停止之前 python 先执行 finally 块然后导致程序停止。
                        这就是为什么你使用 finally 并做一些真正重要的事情。

                        【讨论】:

                          【解决方案15】:

                          运行这些 Python3 代码来观察 finally 的需求:

                          案例1:

                          count = 0
                          while True:
                              count += 1
                              if count > 3:
                                  break
                              else:
                                  try:
                                      x = int(input("Enter your lock number here: "))
                          
                                      if x == 586:
                                          print("Your lock has unlocked :)")
                          
                                          break
                                      else:
                                          print("Try again!!")
                          
                                          continue
                          
                                  except:
                                      print("Invalid entry!!")
                                  finally:
                                      print("Your Attempts: {}".format(count))
                          

                          案例2:

                          count = 0
                          
                          while True:
                              count += 1
                              if count > 3:
                                  break
                              else:
                                  try:
                                      x = int(input("Enter your lock number here: "))
                          
                                      if x == 586:
                                          print("Your lock has unlocked :)")
                          
                                          break
                                      else:
                                          print("Try again!!")
                          
                                          continue
                          
                                  except:
                                      print("Invalid entry!!")
                          
                                  print("Your Attempts: {}".format(count))
                          

                          每次尝试以下输入:

                          1. 随机整数
                          2. 正确的代码是 586(试试这个,你会得到答案)
                          3. 随机字符串

                          ** 处于学习 Python 的早期阶段。

                          【讨论】:

                            【解决方案16】:

                            我试图在我想阅读 excel 表的地方运行代码。问题是,如果有一个文件没有名为的工作表说:SheetSum 我无法将它移动到错误位置!我写的代码是:

                            def read_file(data_file):
                                # data_file = '\rr\ex.xlsx'
                                sheets = {}
                                try:
                                    print("Reading file: "+data_file)
                                    sheets['df_1'] = pd.read_excel(open(data_file,'rb'), 'SheetSum')
                                except Exception as excpt:
                                    print("Exception occurred", exc_info=True)
                                return sheets
                            
                            read_file(file)
                            shutil.move( file, dirpath +'\\processed_files')
                            

                            给出错误:

                            [WinError 32] 进程无法访问该文件,因为它正在 被另一个进程使用

                            我必须添加完整的try except with finally 块并告诉finally 在任何情况下我都需要关闭文件,例如:

                            def read_file(data_file):
                                # data_file = '\rr\ex.xlsx'
                                sheets = {}
                                sheets_file = None
                                try:
                                    print("Reading file: "+data_file)
                                    sheets_file = open(data_file,'rb')
                                    sheets['df_1'] = pd.read_excel(sheets_file, 'SheetSum')
                                except Exception as excpt:
                                    print("Exception occurred", exc_info=True)
                                finally:
                                    if sheets_file:
                                        sheets_file.close()
                                return sheets
                            
                            read_file(file)
                            shutil.move( file, dirpath +'\\processed_files')
                            

                            否则,文件仍然保持打开是背景。

                            如果finally 存在,它指定一个清理处理程序try 子句被执行,包括任何exceptelse 子句。如果 异常发生在任何子句中且未处理,则 暂时保存异常finally 子句被执行。如果 有一个保存的异常,它在finally 的末尾重新引发 条款。如果finally 子句引发另一个异常,则保存的 异常被设置为新异常的上下文。

                            ..更多Here

                            【讨论】:

                            • 你应该使用with open(data_file, 'rb') as src: pd.read_excel(src, 'SheetSum')。它会自动关闭文件
                            • 另外,您应该将此作为单独的问题发布。此空间仅用于回答给定问题
                            【解决方案17】:

                            只是为了让this answer 上的Abhijit Sahu's 评论更容易被看到并带有语法高亮显示:

                            这样,你可以观察在以下情况下哪个代码块会发生什么:

                            try: 
                                x = Hello + 20 
                                x = 10 + 20 
                            except: 
                                print 'I am in except block' 
                                x = 20 + 30 
                            else: 
                                print 'I am in else block' 
                                x += 1 
                            finally: 
                                print 'Finally x = %s' %(x) 
                            

                            【讨论】:

                              猜你喜欢
                              • 1970-01-01
                              • 2015-03-24
                              • 1970-01-01
                              • 1970-01-01
                              • 2023-04-11
                              • 1970-01-01
                              • 2019-06-09
                              • 2022-11-23
                              • 1970-01-01
                              相关资源
                              最近更新 更多