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

 
                
            
         
         浙公网安备 33010602011771号
浙公网安备 33010602011771号