为什么我们需要Python中的“finally”子句?

时间:2012-07-18 23:44:41

标签: python exception-handling try-finally

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

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

与使用finally

相同:

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

我错过了什么吗?

16 个答案:

答案 0 :(得分:319)

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

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语句。

答案 1 :(得分:67)

您可以使用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读者可能觉得有趣的内容。

答案 2 :(得分:21)

它们并不等同。最后,无论发生什么,代码都会运行。它对于必须运行的清理代码很有用。

答案 3 :(得分:9)

要添加上面的其他答案,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子句不管异常都会执行。希望这会有所帮助。

答案 4 :(得分:7)

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

答案 5 :(得分:7)

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

将其与finally:版本进行比较:无论引发任何异常,都保证执行other_code()

答案 6 :(得分:3)

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

我是第二个@ Byers的例子。

答案 7 :(得分:2)

最后也可以在你想要运行时使用&#34;可选&#34;在为主要工作运行代码之前的代码,并且可选代码可能由于各种原因而失败。

在下面的示例中,我们并不确切地知道store_some_debug_info可能引发的异常类型。

我们可以跑:

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

但是,大多数短信都会抱怨过于模糊的异常。此外,由于我们选择仅pass出错,except块并没有真正增加价值。

try:
  store_some_debug_info()
finally:
  do_something_really_important()     

上面的代码与第一段代码具有相同的效果,但更简洁。

答案 8 :(得分:2)

完美的例子如下:

.mat-drawer{
  background-color:red;
}

答案 9 :(得分:2)

正如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子句对于释放外部资源(例如文件或网络连接)非常有用,无论资源的使用是否成功。

答案 10 :(得分:2)

多年来专业地使用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.

答案 11 :(得分:2)

先尝试在没有 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 并做真正重要的事情。

答案 12 :(得分:1)

我试图在我想阅读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 = {}
    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:
        sheets_file.close()
    return sheets

read_file(file)
shutil.move( file, dirpath +'\\processed_files')

否则,文件仍然保持打开状态。

  

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

..更多Here

答案 13 :(得分:0)

这里有一段代码来澄清区别:

...

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

print('Outside')

如果a, b = 2, 1,输出:

In try block 
In finally block 
Outside

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


如果a, b = 2, 0,输出:

In finally block
ZeroDivisionError: division by zero

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


如果a, b = 2, '1',输出:

In except block 
In finally block 
Outside

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

答案 14 :(得分:-1)

try块只有一个强制性子句:try语句。 else,else和finally子句是可选的,并且基于用户首选项。

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

答案 15 :(得分:-1)

运行以下Python3代码以了解最终需求:

案例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的早期阶段。