Python 中的异常 (Exception)
以下 Python 版本为 Python 3.8.10 .
初探异常
错误与异常
错误:
- 语法错误 .
- 逻辑错误 .
异常:程序运行过程中,出现的意料之外的错误(大概类似 corner case),例如除 0(ZeroDivisionError),尾迭代器自增(StopIteration),解包一个 int
等(TypeError),其他异常见「常见异常」.
很明了吧 .
Traceback
我们平时写 Python 码写出 bug 则解释器会报错(运行时),这个就是 Traceback .
处理用户未处理的异常的方法就是先终止程序,再通过 Traceback(堆栈回溯,也称向后追踪)来显示异常发生的上下文 .
我们可以通过引用 traceback
模块来访问 Traceback .
Python 所有异常
Python 异常 | 解释 |
---|---|
BaseException |
所有异常的基类 |
SystemExit |
解释器请求退出 |
KeyboardInterrupt |
用户中断执行(通常是输入 ^C ) |
Exception |
常规错误的基类 |
StopIteration |
迭代器越界 |
GeneratorExit |
生成器异常于是通知退出 |
SystemExit |
Python 解释器请求退出 |
StandardError |
所有的内建标准异常的基类 |
ArithmeticError |
所有数值计算错误的基类 |
FloatingPointError |
浮点计算错误 |
OverflowError |
数值运算超出最大限制(上溢) |
ZeroDivisionError |
除 / 取模零(所有数据类型) |
AssertionError |
断言 (assert) 异常 |
AttributeError |
对象没有这个属性 |
EOFError |
输入读到 EOF |
EnvironmentError |
操作系统错误的基类 |
IOError |
输入 / 输出操作失败 |
OSError |
操作系统错误 |
WindowsError |
系统调用失败 |
ImportError |
导入模块/对象失败 |
LookupError |
无效数据查询的基类 |
IndexError |
序列 (list) 中找不到要调用的索引 |
KeyError |
映射 (map) 中找不到要调用的键 |
MemoryError |
内存溢出 |
NameError |
未声明 / 初始化对象 |
UnboundLocalError |
访问未初始化的本地变量 |
ReferenceError |
弱引用试图访问已经垃圾回收了的对象 |
RuntimeError |
一般的运行时错误 |
NotImplementedError |
调用没有的方法 |
SyntaxError |
语法错误 |
IndentationError |
缩进错误 |
TabError |
Tab 和空格混用 |
SystemError |
一般的解释器系统错误 |
TypeError |
对类型无效的操作 |
ValueError |
传入无效的参数(类型不对) |
UnicodeError |
Unicode 相关的错误 |
UnicodeDecodeError |
Unicode 解码时的错误 |
UnicodeEncodeError |
Unicode 编码时错误 |
UnicodeTranslateError |
Unicode 转换时错误 |
Warning |
所有警告的基类 |
DeprecationWarning |
关于被弃用的特征的警告 |
FutureWarning |
关于构造将来语义会有改变的警告 |
OverflowWarning |
旧的关于自动提升为长整型 (long) 的警告 |
PendingDeprecationWarning |
关于特性将会被废弃的警告 |
RuntimeWarning |
可疑的运行时行为的警告 |
SyntaxWarning |
可疑的语法的警告 |
UserWarning |
用户代码生成的警告 |
异常处理
try... 捕获异常
这个可以类比 C++ 中的 try ... catch
,不过 Python 异常更灵活一点(因为解释性甚至连 C++ 中一些引发编译错误 (Compile Error, CE) 的内容都能补救回来)
平凡的处理方法是 try ... except
:
try:
代码
except 错误类型A as 接受错误信息的变量A:
处理代码A
except 错误类型B as 接受错误信息的变量B:
处理代码B
...
except
里面啥都不填就是自动捕获所有异常 .
接受处理的变量,设为 e
,则可以做:
-
str(e)
/e.message
,只给出异常信息的字符串,不包括异常信息的类型 . -
repr(e)
,给出较全的异常信息,包括异常信息的类型 . -
使用
traceback
模块,此时获取的信息最全,与 Python 解释器运行程序出现错误信息一致,下面是两个常用方法:traceback.print_exc()
打印异常信息到标准错误流 (stderr) .traceback.format_exc()
将同样的输出获取为字符串 .
其余用法请查阅文档 .
-
使用
sys
模块:sys.exc_info()
方法可以获取正在处理的异常信息,即except
子句正在处理的异常,其返回值为一个tuple
类型的三元组(exc_type, exc_value, exc_traceback)
,其中,exc_type
为获取到的异常类型;exc_value
为该异常类型对象;exc_traceback
为一个traceback
对象,包含异常最初发生的调用栈信息 .
例子:
a = 10
b = 0
try:
c = a / b
print(c)
except ZeroDivisionError as e:
print(str(e))
print(repr(e))
print("done")
输出:
division by zero
ZeroDivisionError('division by zero')
done
另外,这个错误类型可以列举,例如:
# Python 3
a = 10
b = 0
try:
c = b / a
print(c)
except (IOError, ZeroDivisionError) as x:
print(x)
else:
print("no error")
print("done")
输出:
0.0
no error
done
我们发现上面的实例中出现了一个 else
字句,这个的意思估计大家也能猜出来吧 —— 没有异常的时候进行的操作 .
一个完整的 try...
语法结构实际上应该包含 except
,else
和 finally
子句,形如:
try:
...
except 异常1 as 异常信息1:
...
except 异常2 as 异常信息2:
...
except:
...
else:
... # 这里是没有异常时执行的
finally:
... # 这里是不管有没有异常最后都执行一下的
我觉得也不需要放实例,非常的明了 .
注意尽量不要在 try ...
语句中使用 raise
,return
这种可能退出程序的语句,例如:
def foo(x):
try:
x += 1
return x
finally:
return x+1
print(foo(11))
输出:
13
这说明,try ... except
语句中使用 return
实际上 finally
子句是会执行的 .
具体的:
- 在函数中的
try ... except
语句使用return
后,仍然会执行finally
中的内容 . - 在
finally
使用return
会导致异常无法回溯 .
还有就是关于性能了,我们尽量是少让它 try ... except
,例如下面这个实例:
for i in range(114514):
try:
pass
except:
pass
就可以改写为
try:
for i in range(114514):
pass
except:
pass
raise 抛出异常
raise
可以抛出一个异常,例如:
print(114514)
raise SyntaxError
print(1919810)
这样中间就会出一个 SyntaxError
.
SyntaxError
实际上是一个类,抛出异常时,会自动生成它的一个对象,Python 实际上抛出的是这个对象 .
当然,也可以自行生成对象:
raise SyntaxError()
结语
我觉得异常还是 Python 中比较基础的知识点,希望大家掌握 .
以下是 OI 特供版内容:
在 OI 中的应用
不定行读入
我们知道 Python 读没了会抛出 EOFError .
所以 try ... except
一下就可以判断读没读完,也就可以实现不定行读入了 .
中缀表达式
中缀表达式
输入一个中缀表达式(由 \(0\dots9\) 组成的运算数,加
+
减-
乘*
除/
(除为整除)四种运算符,左右小括号组成 . 注意-
也可作为负数的标志,表达式以@
作为结束符),判断表达式是否合法,如果不合法,输出NO
;否则请把表达式转换成后缀形式,再求出后缀表达式的值并输出 .
黑盒测试不用管转换后缀表达式,只需要求值即可 .
一开始先把 @
去掉,这个非常平凡 .
然后 Python 有方法 eval
可以返回一行 Python 语句返回的结果,就像在解释器中直接输入一样 .
这样我们就可以直接使用 eval
来计算了 .
注意这里的除是整除所以要先把 /
全部 replace
成 //
,这样才符合 Python 语法 .
eval
如果运行不下去了就会抛出异常,我们根据这个来判断 NO
即可 .
注意抛出的不一定是 SyntaxError
,比如下面这组数据:
(5*(5-9)-4)+7-9*2+5(*(+6-2))@
eval
会抛出 TypeError
.
下面是完整代码:
import traceback
s = input().split('@')[0].replace("/", "//")
try:
print(eval(s))
except:
print("NO")
traceback.print_exc() # debug
以下是博客签名,正文无关
本文来自博客园,作者:yspm,转载请注明原文链接:https://www.cnblogs.com/CDOI-24374/p/16611179.html
版权声明:本作品采用「署名-非商业性使用-相同方式共享 4.0 国际」许可协议(CC BY-NC-SA 4.0)进行许可。看完如果觉得有用请点个赞吧 QwQ