在 Python 的 try 块中使用 finally 的实际例子是什么
What could be practical examples of using finally in the try block in Python
什么时候在 try..except
块中使用 finally
有意义? try..except
后面的 listing 语句不也是一样吗?
这两个有什么区别?
try:
result = 100 / 0
except ZeroDivisionError:
print("division by zero!")
else:
print("result is", result)
finally:
print("final statement")
对比
try:
result = 100 / 0
except ZeroDivisionError:
print("division by zero!")
else:
print("result is", result)
print("final statement")
Isn't listing statements just after the try..except doing the same?
不,不是。您假设您已经涵盖了块可以退出的所有方式。
finally
保证执行即使块退出。这包括 return
、continue
或 break
,不仅是例外。
对于您的具体示例,您涵盖了几乎 try
块之外的所有可能路径。但是您没有涵盖 KeyboardInterrupt
或 MemoryError
。如果有人在执行过程中点击 CTRL-C
,只有第一个例子会执行 print("some code at last")
行。
那是因为您的代码没有捕捉到 KeyboardInterrupt
或 MemoryError
,但这些异常仍可能发生。如果 result = 100 / 0
语句引发了其中一个异常,则不会捕获异常并且 整个框架将退出 。但不是在执行 finally:
子句之前。
用另一个例子更容易演示,一个不捕获异常的例子:
mapping = {42: "The answer"}
try:
result = mapping[42] / 17
except KeyError:
print("Oops, no key 42 defined!")
else:
print(result)
finally:
del mapping
此处,finally
语句 将被执行 ,即使上面引发了 TypeError
异常:
>>> mapping = {42: "The answer"}
>>> try:
... result = mapping[42] / 17
... except KeyError:
... print("Oops, no key 42 defined!")
... else:
... print(result)
... finally:
... del mapping
...
Traceback (most recent call last):
File "<stdin>", line 2, in <module>
TypeError: unsupported operand type(s) for /: 'str' and 'int'
>>> mapping # was deleted in the `finally` block
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
NameError: name 'mapping' is not defined
这就是 finally
通常的用途,清理资源。您甚至可以将它与 return
退出函数一起使用:
>>> def foo():
... try:
... return 42
... finally:
... print("Returning doesn't stop finally from executing!")
... print("This is never reached")
...
>>> foo()
Returning doesn't stop finally from executing!
42
请注意 Python 也有 with
statement and context managers 来帮助完成同样的工作。上下文管理器 封装 通常在 finally
中完成的清理工作,让您不必在较长的代码段末尾查找 finally
块例如,检查文件是否已关闭。
所以代替:
fileobj = open(filename)
try:
with line in fileobj:
# many lines of parsing code
# .
# etc.
# .
finally:
fileobj.close()
您可以使用文件对象作为上下文管理器,上面的内容被简化为:
with open(filename) as fileobj:
with line in fileobj:
# many lines of parsing code
# .
# etc.
# .
什么时候在 try..except
块中使用 finally
有意义? try..except
后面的 listing 语句不也是一样吗?
这两个有什么区别?
try:
result = 100 / 0
except ZeroDivisionError:
print("division by zero!")
else:
print("result is", result)
finally:
print("final statement")
对比
try:
result = 100 / 0
except ZeroDivisionError:
print("division by zero!")
else:
print("result is", result)
print("final statement")
Isn't listing statements just after the try..except doing the same?
不,不是。您假设您已经涵盖了块可以退出的所有方式。
finally
保证执行即使块退出。这包括 return
、continue
或 break
,不仅是例外。
对于您的具体示例,您涵盖了几乎 try
块之外的所有可能路径。但是您没有涵盖 KeyboardInterrupt
或 MemoryError
。如果有人在执行过程中点击 CTRL-C
,只有第一个例子会执行 print("some code at last")
行。
那是因为您的代码没有捕捉到 KeyboardInterrupt
或 MemoryError
,但这些异常仍可能发生。如果 result = 100 / 0
语句引发了其中一个异常,则不会捕获异常并且 整个框架将退出 。但不是在执行 finally:
子句之前。
用另一个例子更容易演示,一个不捕获异常的例子:
mapping = {42: "The answer"}
try:
result = mapping[42] / 17
except KeyError:
print("Oops, no key 42 defined!")
else:
print(result)
finally:
del mapping
此处,finally
语句 将被执行 ,即使上面引发了 TypeError
异常:
>>> mapping = {42: "The answer"}
>>> try:
... result = mapping[42] / 17
... except KeyError:
... print("Oops, no key 42 defined!")
... else:
... print(result)
... finally:
... del mapping
...
Traceback (most recent call last):
File "<stdin>", line 2, in <module>
TypeError: unsupported operand type(s) for /: 'str' and 'int'
>>> mapping # was deleted in the `finally` block
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
NameError: name 'mapping' is not defined
这就是 finally
通常的用途,清理资源。您甚至可以将它与 return
退出函数一起使用:
>>> def foo():
... try:
... return 42
... finally:
... print("Returning doesn't stop finally from executing!")
... print("This is never reached")
...
>>> foo()
Returning doesn't stop finally from executing!
42
请注意 Python 也有 with
statement and context managers 来帮助完成同样的工作。上下文管理器 封装 通常在 finally
中完成的清理工作,让您不必在较长的代码段末尾查找 finally
块例如,检查文件是否已关闭。
所以代替:
fileobj = open(filename)
try:
with line in fileobj:
# many lines of parsing code
# .
# etc.
# .
finally:
fileobj.close()
您可以使用文件对象作为上下文管理器,上面的内容被简化为:
with open(filename) as fileobj:
with line in fileobj:
# many lines of parsing code
# .
# etc.
# .