428

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

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

与此相同,使用finally

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

我错过了什么吗?

4

17 回答 17

567

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

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语句。
于 2012-07-18T23:46:02.240 回答
122

You can use finally to make sure files or resources are closed or released regardless of whether an exception occurs, even if you don't catch the exception. (Or if you don't catch that specific exception.)

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

In this example you'd be better off using the with statement, but this kind of structure can be used for other kinds of resources.

A few years later, I wrote a blog post about an abuse of finally that readers may find amusing.

于 2012-07-18T23:51:20.630 回答
32

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


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

于 2012-07-18T23:46:12.380 回答
27

要添加到上面的其他答案,该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无论是否有异常,该子句都会执行。希望这可以帮助。

于 2017-12-13T06:46:06.257 回答
11

文档中所述,该子句旨在定义在所有情况下都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 子句对于释放外部资源(如文件或网络连接)很有用,无论资源的使用是否成功。

于 2017-12-03T13:20:56.213 回答
8

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

于 2012-07-18T23:46:36.663 回答
7

In your first example, what happens if run_code1() raises an exception that is not TypeError? ... other_code() will not be executed.

Compare that with the finally: version: other_code() is guaranteed to be executed regardless of any exception being raised.

于 2012-07-18T23:47:41.467 回答
7

这是一段代码来澄清差异:

...

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块将是多余的。

于 2021-08-09T10:05:10.800 回答
5

多年来,专业地使用 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.
于 2018-03-16T06:12:18.040 回答
4

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

在下面的示例中,我们并不确切知道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()     

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

于 2017-02-17T17:14:46.730 回答
3

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

我第二个@Byers 的例子。

于 2015-05-15T17:59:29.023 回答
3

完美的例子如下:

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)
于 2017-10-02T06:33:36.210 回答
3

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

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

于 2018-10-07T04:10:27.783 回答
3

尝试在没有 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 并做一些非常重要的事情。

于 2021-03-09T17:13:02.100 回答
2

运行这些 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 的早期阶段。

于 2019-03-29T12:58:59.100 回答
0

我试图在我想阅读 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子句引发另一个异常,则将保存的异常设置为新异常的上下文。

..更多在这里

于 2019-12-22T10:53:24.043 回答
0

只是为了让Abhijit Sahu对这个答案的评论更好地被看到并且语法突出显示:

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

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) 
于 2021-10-19T14:08:58.220 回答