资料来源: https://book./details/321/
介绍
在编程过程中为了增加友好性,在程序出现bug时一般不会将错误信息显示给用户让用户蒙逼,而是显示一个更友好的提示信息。
语法
try:
"""your code"""
except Exception:
"""上面的程序执行出错后就指行这里的代码"""
需求:将用户输入的两个数字相加
while True:
num1 = input('num1:')
num2 = input('num2:')
try:
num1 = int(num1)
num2 = int(num2)
result = num1 + num2
except Exception as e:
print('出现异常,信息如下:')
print(e)
输入的不是数字的话,执行出错的异常就会把捕捉到 
像上面这个Exception异常,几乎能捕捉到所有的错误,这不一定是好事,因为程序出错的原因有很多种,我可能希望出不同的错误就执行不同的异常处理逻辑。 全执行同一逻辑的话会增加程序调试难度,因为你不知道是什么原因导致的错误
异常类型
内置异常类的继承关系
BaseException ------------------------------------- 所有异常的基类
+-- SystemExit ----------------------------------- 解释器请求退出
+-- KeyboardInterrupt --------------------------- 用户中断执行(通常是输入^C)
+-- GeneratorExit -------------------------------- 生成器(generator)发生异常来通知退出
+-- Exception ----------------------------------- 常规错误的基类
+-- StopIteration --------------------------- 迭代器没有更多的值
+-- StopAsyncIteration
+-- ArithmeticError ------------------------ 所有数值计算错误的基类
| +-- FloatingPointError ----------------- 浮点计算错误
| +-- OverflowError ---------------------- 数值运算超出最大限制
| +-- ZeroDivisionError ------------------ 除(或取模)零 (所有数据类型)
+-- AssertionError -------------------------- 断言语句失败
+-- AttributeError -------------------------- 对象没有这个属性
+-- BufferError
+-- EOFError -------------------------------- 没有内建输入,到达EOF 标记
+-- ImportError ----------------------------- 导入模块/对象失败
| +-- ModuleNotFoundError
+-- LookupError ----------------------------- 无效数据查询的基类
| +-- IndexError ------------------------- 序列中没有此索引(index)
| +-- KeyError --------------------------- 映射中没有这个键
+-- MemoryError ----------------------------- 内存溢出错误(对于Python 解释器不是致命的)
+-- NameError ------------------------------- 未声明/初始化对象 (没有属性)
| +-- UnboundLocalError ------------------ 访问未初始化的本地变量
+-- OSError --------------------------------- 操作系统错误
| +-- BlockingIOError
| +-- ChildProcessError
| +-- ConnectionError
| | +-- BrokenPipeError
| | +-- ConnectionAbortedError
| | +-- ConnectionRefusedError
| | +-- ConnectionResetError
| +-- FileExistsError
| +-- FileNotFoundError
| +-- InterruptedError
| +-- IsADirectoryError
| +-- NotADirectoryError
| +-- PermissionError
| +-- ProcessLookupError
| +-- TimeoutError
+-- ReferenceError -------------------------- 弱引用(Weak reference)试图访问已经垃圾回收了的对象
+-- RuntimeError ---------------------------- 一般的运行时错误
| +-- NotImplementedError ---------------- 尚未实现的方法
| +-- RecursionError
+-- SyntaxError ----------------------------- Python 语法错误
| +-- IndentationError ------------------- 缩进错误
| +-- TabError ---------------------- Tab 和空格混用
+-- SystemError ----------------------------- 一般的解释器系统错误
+-- TypeError ------------------------------- 对类型无效的操作
+-- ValueError ------------------------------ 传入无效的参数
| +-- UnicodeError ----------------------- Unicode 相关的错误
| +-- UnicodeDecodeError ------------ Unicode 解码时的错误
| +-- UnicodeEncodeError ------------ Unicode 编码时错误
| +-- UnicodeTranslateError --------- Unicode 转换时错误
+-- Warning --------------------------------- 警告的基类
+-- DeprecationWarning ----------------- 关于被弃用的特征的警告
+-- PendingDeprecationWarning ---------- 关于特性将会被废弃的警告
+-- RuntimeWarning --------------------- 可疑的运行时行为(runtime behavior)的警告
+-- SyntaxWarning ---------------------- 可疑的语法的警告
+-- UserWarning ------------------------ 用户代码生成的警告
+-- FutureWarning ---------------------- 关于构造将来语义会有改变的警告
+-- ImportWarning
+-- UnicodeWarning
+-- BytesWarning
+-- ResourceWarning
常见异常类型
- AttributeError 试图访问一个对象没有的属性,比如foo.x,但是foo没有属性x
class A:
pass
try:
a = A()
print(a.name) # name不存在
except AttributeError as e:
print(e) # 'A' object has no attribute 'name'
- IOError 输入/输出异常;基本上是无法打开文件
try:
f = open("不存在的文件.txt", mode="r", encoding="utf-8")
except IOError as e:
print(e) # [Errno 2] No such file or directory: '不存在的文件.txt'
- ImportError 无法引入模块或包;基本上是路径问题或名称错误
import importlib
try:
metaclass = importlib.import_module("import_lib.metaclasss")
except ImportError as e:
print(e) # No module named 'import_lib.metaclasss'
- IndexError 下标索引超出序列边界,比如当x只有三个元素,却试图访问x[5]
try:
a = [1, 2, 3]
print(a[9])
except IndexError as e:
print(e)
try:
a = {"name": "张小小"}
print(a["age"])
except KeyError as e:
print(e) # age
- KeyboardInterrupt Ctrl+C被按下
"""
命令行程序运行期间,如果用户想终止程序,一般都会采用Ctrl-C快捷键,
这个快捷键会引发python程序抛出KeyboardInterrupt异常。
我们可以捕获这个异常,在用户按下Ctrl-C的时候,进行一些清理工作。
注意,携程except Exception将无法捕获KeyboardInterrupt异常。
"""
try:
# many code here
except KeyboardInterrupt as e:
# do something
try:
print(aa) # aa未定义
except NameError as e:
print(e) # name 'aa' is not defined
try:
print(float(['3']))
except TypeError as e:
print(e) # float() argument must be a string or a number, not 'list'
- ValueError 传入一个调用者不期望的值,即使值的类型是正确的
"""
float函数可以接受字符串,即float('5'),只是float('string')中的值'string'是不合适的(不可转换的)字符串
另一方面
因此,如果您尝试float(['5']),就会得到一个TypeError,因为列表永远不能转换为浮点。
"""
try:
print(float('ooo'))
except ValueError as e:
print(e) # could not convert string to float: 'ooo'
import math
try:
print(math.sqrt(-10))
except ValueError as e:
print(e) # math domain error
- UnboundLocalError 试图访问一个还未被设置的局部变量,基本上是由于另有一个同名的全局变量,导致你以为正在访问它
- IndentationError 语法错误(的子类);代码没有正确对齐
- SyntaxError Python代码非法,代码不能编译(个人认为这是语法错误,写错了)
捕获多个异常
写程序时需要考虑到try代码块中可能出现的任意异常,可以这样写:
s1 = 'hello'
try:
int(s1)
except IndexError as e:
print(e)
except KeyError as e:
print(e)
except ValueError as e:
print(e)
如果上面3个异常依然没有匹配到对应的错误 怎么办? 可以在程序最后加上Exception这个万能异常。
一个except还可以同时包含多个异常,即相当于我们对某几个不同的异常,采用同样的处理 except后面跟着的多个异常类型,要把它们写成tuple的形式。
import os
def test_exception2(num):
try:
a = 1/num
if num == 1: os.remove('NotExistedFile')
except (ZeroDivisionError, FileNotFoundError) as e:
print('3---', repr(e))
test_exception2(0)
ZeroDivisionError('division by zero')
test_exception2(1)
FileNotFoundError(2, 'No such file or directory')
其它异常结构
try:
# 主代码块
pass
except KeyError,e:
# 异常时,执行该块
pass
else:
# 主代码块执行完,若未触发任何异常,执行该块
pass
finally:
# 无论监测的代码是否发生异常,都执行该处代码
pass
主动触发异常raise
try:
raise Exception('错误了。。。')
except Exception as e:
print(e)
raise是更好的return
该资料来源:https://www./archives/1175
编写软件,大部分代码都是在处理各种异常和错误。我们常常会遇到这样的场景,代码流程需要一层层的判断底层的返回是否成功, 这样的代码写起来其实很费劲,为了一个可能出现的错误,要在每一个获取返回值的地方写if判断。 其实,这个时候,使用raise来抛出一个异常,比用return返回标志位(True或False),更加简单,代码的可读性和可维护性也更好,代码的层次感也越强。
return语句只能返回到上一层调用的地方,如果调用层次比较多,底层的问题,要层层传递上来就太费劲了, 这样代码写起来看起来都很别扭。return主要还是用来返回数据的,而raise是更好的“返回异常”的方式。
在一个处于层层调用关系的流程中,不管哪个地方raise抛出一个异常,我们只需要在流程需要的地方try…except…捕获异常, 就可以了。raise抛出异常后,代码返回到最近的try…except…的地方(这是个与return很不一样的细节),这样中间流程的代码, 写起来就会很轻松惬意优雅。而且,如果中间虽捕获了异常,但是不对异常进行处理,也可以直接独立的依据raise,再次将异常抛出,交给更上层来处理。
举个例子:
def level_1():
raise ValueError("this is a value error")
def level_2():
level_1()
print("in level 2")
def level_3():
level_2()
print("in level 3")
def top():
level_3()
print("in top")
try:
top()
except:
print("catch exception from level 1")
# 输出:catch exception from level 1
以上示例代码,在最底层的函数raise一个ValueError异常,top函数与直接raise异常的函数,中间还经过了两层调用。 不过,运行程序发现,最底层raise之后,在最顶层直接捕获异常,而且,很重要的细节是,代码中所有的打印都没有执行, 代码相当于从最底层直接return到了最顶层try…except…的地方。
这种代码的写法,比一层层return再判断,要简单很多。这种层层调用在软件中很常见,稍微封装一下底层接口代码, 层次关系就出现了。如果再学会了自定义Python的异常类,配合这种写法,您的代码一定会更加漂亮性感!
单独一句raise的作用
代码中常常能看到单独使用一句raise,后面不带任何参数,这样写的作用是,将向下文当前的异常抛出(raise语句不带参数的默认动作)。
def do_raise():
raise ValueError('test value error')
def middle():
try:
do_raise()
except:
print('something wrong')
raise
def top():
try:
middle()
except ValueError as e:
print(repr(e))
top()
在middle函数中,单独使用raise语句,它将会被do_raise抛出的异常,直接在此抛出。middle函数不对此异常进行处理,而是交给上层代码去处理。这段代码的运行效果如下:
something wrong
ValueError('test value error')
raise在层层调用的代码流程中,简化了异常处理的代码编写,并形成了自己独有的异常处理层次关系,使得代码在处理异常时非常灵活高效。
自定义异常
class MyException(BaseException): # BaseException是所有异常的基类
def __init__(self,msg):
self.message = msg
def __str__(self):
return self.message
try:
raise MyException("我的错误")
except MyException as e:
print(e)
断言
- assert语法用于判断代码是否符合执行预期
- 一般来说在做单元测试的时候用的比较多,在生产环境代码运行的情况下,不建议使用断言,会让程序abort掉。
assert type(1) is int
assert 1+1 == 2
assert 1+1 == 3 # 会报AssertionError
应用场景举例,别人调你的接口,你的接口要求他调用时必须传递指定的关键参数,等他传递进来时,你就可以用用assert语句他传的参数是否符合你的预期
def my_interface(name,age,score):
assert type(name) is str
assert type(age) is int
assert type(score) is float
my_interface("alex",22,89.2)
|