try-except内部的更改在捕获异常后仍然存在

Cam*_*ilB 9 python exception-handling



因为我第一次学习了异常处理(不是在Python中),所以我觉得当你开始一个try块时,就像你开始在沙盒中写一样:如果发生异常,那么发生在尝试块会像它从未发生过一样.令我天真的惊讶,我注意到这不是真的,或者不是我认为的方式,至少在Python中.这是我在Python中的实验:

>>> a = range(5)
>>> a
[0, 1, 2, 3, 4]
>>> try:
...     a.append(5)
...     oops
... except:
...     raise
... 
Traceback (most recent call last):
  File "<stdin>", line 3, in <module>
NameError: name 'oops' is not defined
>>> print a
[0, 1, 2, 3, 4, 5]
Run Code Online (Sandbox Code Playgroud)

如您所见,我更改了try块中的列表,然后触发了一个错误,该错误被引发.我期待看到原始形式的列表[0, 1, 2, 3, 4],但a.append(5)持续存在.

我的期望首先是错的吗?也许部分错误的期望(可能有一个沙箱,但它不是那样)?

650*_*502 6

您刚刚发现异常不是错误处理的灵丹妙药.

异常不会保护您免受状态更改...在抛出异常之前完成的任何内容都必须撤消.这也是Python,C++,Java和许多其他语言中的异常工作方式.

有时您可能会有一种"外部"一般保护:例如,如果您所做的只是对支持事务的数据库进行更改,那么您可以使用顶级catch语句执行"回滚"而不是提交更改而您得到你正在寻找的保护.如果没有这样一个自然的"墙"可以防止部分状态变化,那么事情就更难以正确处理.

已经完成的操作无法撤消的原因是,由于问题的复杂性不断扩大,因此使用异常并非易事.

通常,代码可以在几个级别分类为"安全"异常:

  1. 如果发生异常,一切都会毁坏,甚至不能进行干净的退出或重启.这通常被归类为非例外安全.

  2. 如果发生异常,代码将无法完成其工作,并且子系统的状态(类实例,库)无效.但是,您可以安全地重新启动(例如,您可以销毁实例或重新初始化库).这是极小的异常安全性.

  3. 如果出现异常,代码将无法完成其工作,子系统的状态将有效但未指定.例如,调用代码可以尝试检查当前状态并继续使用子系统而不是重新初始化它.比2好一点.

  4. 如果发生异常,代码将不执行任何操作,使程序状态保持不变.因此,请求完成没有错误或错误信号返回给调用者并且没有任何改变.这当然是最好的行为.

异常处理的最大问题是,即使你有两个非常安全的4型部件A,B但简单的顺序组合仍然AB不安全,因为如果出现问题,B你还必须撤消A已经完成的任何事情.此外,如果在执行时可能会出现异常1/A(即,当A不能完成所能完成的事情时),那么你就会遇到大麻烦,因为你既不能做B也不能恢复之前的状态(这意味着它只是不可能)实现AB为类型4操作).

换句话说,好的砖块不会做出微不足道的好结构(关于异常安全).