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

Posted

技术标签:

【中文标题】为啥我们需要 Python 中的“finally”子句?【英文标题】: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()

我错过了什么吗?

【问题讨论】:

【参考方案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))

每次尝试以下输入:

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

** 处于学习 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) 

【讨论】:

以上是关于为啥我们需要 Python 中的“finally”子句?的主要内容,如果未能解决你的问题,请参考以下文章

为啥我的 Vue 项目中的 promise.finally 不能在 Edge 中运行?

java中为啥用finally语句

python中的try/except/else/finally语句

python中的try/except/else/finally语句

为啥在 Try ... Catch 中使用 finally

求教大神,java中的jdbc程序为啥要加finally,不是加了try catch以后,后面的语句就会执行了啊