为啥我们需要 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
。
其他控制流语句,例如continue
和break
语句。
【讨论】:
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
子句被执行,包括任何except
和else
子句。如果 异常发生在任何子句中且未处理,则 暂时保存异常。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
子句被执行,包括任何except
和else
子句。如果 异常发生在任何子句中且未处理,则 暂时保存异常。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 中运行?
python中的try/except/else/finally语句