由于我第一次学习了异常处理(不是在Python中),我觉得当你开始一个尝试块时,就像你开始在沙盒中写一样:如果是异常的话发生时,尝试块内发生的一切都会像从未发生过一样。令我天真的惊讶,我注意到这不是真的,或者不是我认为的方式,至少在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]
如您所见,我更改了尝试块内的列表,然后触发了引发的错误。我希望看到列表的原始格式为[0, 1, 2, 3, 4]
,但a.append(5)
仍然存在。
首先,我的期望是错误的吗?也许是部分错误的期望(可能有一个沙盒,但它不是那样的行为)?
答案 0 :(得分:6)
您刚刚发现异常不是错误处理的灵丹妙药。
异常不会保护您免受状态更改...在抛出异常之前完成的任何内容都必须撤消。这就是Python,C ++,Java和许多其他语言中的异常工作方式。
有时您可能会有一种“外部”一般保护:例如,如果您所做的只是对支持事务的数据库进行更改,那么您可以使用顶级catch语句来执行“回滚”而不是提交变化,你得到你正在寻找的保护。如果没有这样一个自然的“墙”可以防止部分状态变化,那么事情就更难以正确处理。
由于问题的复杂性不断扩大,所以已经完成的操作不会被撤消的原因在于,使用异常并非易事。
通常,代码可以在几个级别分类为“安全”异常:
如果发生异常,一切都会毁坏,甚至不能进行干净的退出或重启。这通常被归类为非例外安全。
如果出现异常,代码将无法完成其作业,并且子系统(类实例,库)的状态无效。但是,您可以安全地重新启动(例如,您可以销毁实例或重新初始化库)。这是极小的异常安全性。
如果发生异常,代码将无法完成其工作,子系统的状态将有效但未指定。例如,调用代码可以尝试检查当前状态并继续使用子系统而不是重新初始化它。比2好一点。
如果出现异常,代码将不执行任何操作,保持程序状态不变。因此,请求完成没有错误或错误信号返回给调用者并且没有任何改变。这当然是最好的行为。
异常处理的最大问题是即使你有两个非常安全的4型A
和B
仍然是简单的顺序组合AB
是不安全的,因为如果是B
中的问题您还必须撤消A
已完成的任何内容。
另外,如果在执行1/A
时可能会出现异常(即,当A
无法完成时),那么你就会遇到大麻烦,因为你不能B
,也不像以前那样恢复状态(这意味着将AB
实现为类型4操作是不可能的。)
换句话说,好的砖块不会制造出很好的结构(就例外安全而言)。
答案 1 :(得分:2)
您对任何支持异常的语言的期望都是错误的 - 没有与try块相关的全有或全无语义(尽管您可能在某些语言中有事务概念,例如,如果支持事务性内存)。
只有在异常之后的try块的部分不再执行。
答案 2 :(得分:2)
是的,您的假设不正确。但是,您可以做的一件好事是使用with
清除某些类型。请参阅Python's tutorial。
我唯一看到过你的错误回滚沙盒的想法就是函数式语言。
答案 3 :(得分:2)
你的期望是错误的。也许这将是一个很好的功能(它存在于名称事务中 - 例如在SQL中 - 但它在编程语言中很少见,而且大多数研究都发生在没有人使用的语言中),但是任何人都不会使用任何语言都不可能(更不用说 - 因为编译器/解释器通常对程序员的工作知之甚少,所以你必须保存整个程序的状态并恢复它,这仍然会遗漏解释器之外的所有副作用,例如文件I / O)。 / p>
try
块只是意味着“尝试执行此操作,如果失败,请跳过此”。请注意,只有后半部分是特殊的 - 如果异常发生在try
块之外,执行会将调用图跳转到下一个try
,或者 - 如果没有 - 则跳转到某个全局处理程序打印异常并结束程序的执行。