Python基础知识—异常检查和处理
1、如何控制异常(try_except)
1)捕捉潜在错误
- 比如读了一个不存在的文件
with open("no_file.txt") as f:
print(f.read)
FileNotFoundError: [Errno 44] No such file or directory: 'no_file.txt'
可以看到上面报错中有一个关键词FileNotFoundError
,这就是我们要捕捉的错误类型。
try:
with open("no_file.txt", "r") as f:
print(f.read())
except FileNotFoundError as e:
print(e)
with open("no_file.txt", "w") as f:
f.write("I'm no_file.txt")
print("new file 'no_file.txt' has been written")
[Errno 44] No such file or directory: 'no_file.txt'
new file 'no_file.txt' has been written
可以发现程序已经不报错了,因为错误类型被捕捉。捕捉之后,我们可以进行其他相关的操作,比如创建这个不存在的文件
2)处理多个异常
- 可以在
except
后面多写几个异常的种类,程序会按照我们所写的顺序去一次检测异常,并捕捉第一个遇到的异常
d = {"name", "f1", "age": 2}
l = [1, 2, 3]
try:
v = d["gender"]
l[3] = 4
except (KeyError, IndexError) as e:
print("key or index error for:", e)
key or index error for: 'gender'
- 若修改一下,字典中添加一个
gender
,就会变成IndexError
异常了
d = {"name": "f1", "age": 2, "gender": 1}
l = [1,2,3]
try:
v = d["gender"]
l[3] = 4
except (KeyError, IndexError) as e:
print("key or index error for:", e)
key or index error for: list assignment index out of range
- 当然也可以写俩个
except
区分异常
d = {"name", "f1", "age": 2}
l = [1, 2, 3]
try:
v = d["gender"]
l[3] = 4
except KeyError as e:
print("key error for:", e)
d["gender"] = "x"
except IndexError as e:
print("index error for:", e)
l.append(4)
print(d)
print(l)
key error for: 'gender'
{'name': 'f1', 'age': 2, 'gender': 'x'}
[1, 2, 3]
!!!这时我们会发现,程序不会同时处理所有的错误,也就是说,当程序检查到了keyError
错误之后,就捕捉了这个错误(中止错误之后的代码),然后跳入except
中去做了相应的处理。
所以第二个列表索引被触发的条件是:只有当字典正常的时候,列表的报错才会被触发
3)try-except-else
- 其中首先去执行
try
中的代码,然后如果有错,则会捕捉异常,进入except
代码段,否则程序无错就将进入else
代码段,注意:except
和else
代码段是二者挑其一
l = [1, 2, 3]
try:
l[3] = 4
except IndexError as e:
print(e)
else:
print("no error, now in else")
list assignment index out of range
- 随后,改正错误,使得程序段无误
l = [1,2,3,4]
try:
l[3] = 4
except IndexError as e:
print(e)
else:
print("no error, now in else")
4)try-except-finally
-
3中的else是当程序无错误时执行的,而
finally
就是不管有没有异常,最后都必须执行finally
中的代码。也就是说,有异常,执行except下面的代码,随后执行finally下的代码,反之直接执行finally下的代码
l = [1, 2, 3]
try:
l[3] = 4
except IndexError as e:
print(e)
finally:
print("reach finally")
list assignment index out of range
reach finally
l = [1,2,3,4]
try:
l[3] = 4
except IndexError as e:
print(e)
finally:
print("reach finally")
reach finally
5)raise手动触发异常
通俗理解,自己去触发异常,而非程序判断出错时返回异常
自己触发异常的好处,可以更改异常提示信息,可以告诉自己or他人为什么错了,哪里错了
def no_negative(num):
if num < 0:
raise ValueError("I said no negative")
return num
print(no_negative(-1))
ValueError: I said no negative
6)Python异常错误名称表
可以碰到需要了再去查,记住几个常见的就好
异常名称 | 描述 |
---|---|
BaseException | 所有异常的基类 |
SystemExit | 解释器请求退出 |
KeyboardInterrupt | 用户中断执行(通常是输入^C) |
Exception | 常规错误的基类 |
StopIteration | 迭代器没有更多的值 |
GeneratorExit | 生成器(generator)发生异常来通知退出 |
StandardError | 所有的内建标准异常的基类 |
ArithmeticError | 所有数值计算错误的基类 |
FloatingPointError | 浮点计算错误 |
OverflowError | 数值运算超出最大限制 |
ZeroDivisionError | 除(或取模)零 (所有数据类型) |
AssertionError | 断言语句失败 |
AttributeError | 对象没有这个属性 |
EOFError | 没有内建输入,到达EOF 标记 |
EnvironmentError | 操作系统错误的基类 |
IOError | 输入/输出操作失败 |
OSError | 操作系统错误 |
WindowsError | 系统调用失败 |
ImportError | 导入模块/对象失败 |
LookupError | 无效数据查询的基类 |
IndexError | 序列中没有此索引(index) |
KeyError | 映射中没有这个键 |
MemoryError | 内存溢出错误(对于Python 解释器不是致命的) |
NameError | 未声明/初始化对象 (没有属性) |
UnboundLocalError | 访问未初始化的本地变量 |
ReferenceError | 弱引用(Weak reference)试图访问已经垃圾回收了的对象 |
RuntimeError | 一般的运行时错误 |
NotImplementedError | 尚未实现的方法 |
SyntaxError | Python 语法错误 |
IndentationError | 缩进错误 |
TabError | Tab 和空格混用 |
SystemError | 一般的解释器系统错误 |
TypeError | 对类型无效的操作 |
ValueError | 传入无效的参数 |
UnicodeError | Unicode 相关的错误 |
UnicodeDecodeError | Unicode 解码时的错误 |
UnicodeEncodeError | Unicode 编码时错误 |
UnicodeTranslateError | Unicode 转换时错误 |
Warning | 警告的基类 |
DeprecationWarning | 关于被弃用的特征的警告 |
FutureWarning | 关于构造将来语义会有改变的警告 |
OverflowWarning | 旧的关于自动提升为长整型(long)的警告 |
PendingDeprecationWarning | 关于特性将会被废弃的警告 |
RuntimeWarning | 可疑的运行时行为(runtime behavior)的警告 |
SyntaxWarning | 可疑的语法的警告 |
UserWarning | 用户代码生成的警告 |
2、单元测试
测试非常重要!!!由于在开发大项目的时候!!!
1)什么是单元测试(Unittest)
很多语言都有Unittest,但每一种语言的Unittest方法都不一样,在Python中,我们常用一个原生的unittest
来做单元测试。
单元测试:比如你在开发一个QQ,它包含了很多功能,你想测试其中的一个小功能,比如测试添加好友,这便叫单元测试。只有保证每个单元测试都正常,最终的项目才能顺利成功通过。
- 一般我们都会写完程序之后运行它,才能知道它到底有没有出错,如有错误那么再去检查
这种方式比较适合:
小型项目
较少的功能的项目
项目功能之间并不会有任何联系
- 否则,使用Unittest将会是一个非常明智的选择
import unittest
def my_div(a, b):
return a / b
class TestFunc(unittest.TestCase):
def test_div(self):
self.assertEqual(my_div(2, 1), 2)
self.assertEqual(my_div(2, -1), -2)
unittest.main()
.
----------------------------------------------------------------------
Ran 1 test in 0.000s
OK
import unittest
def my_div(a, b):
return a / b
class TestFunc(unittest.TestCase):
def test_div(self):
self.assertEqual(my_div(2, 0), 1)
unittest.main()
E
======================================================================
ERROR: test_div (__main__.TestFunc)
----------------------------------------------------------------------
Traceback (most recent call last):
File "/test.file.py", line 10, in test_div
self.assertEqual(my_div(2, 0), 1)
File "/test.file.py", line 5, in my_div
return a / b
ZeroDivisionError: division by zero
----------------------------------------------------------------------
Ran 1 test in 0.000s
FAILED (errors=1)
2)unittest规范
首先,unittest仅供开发的时候使用测试,不会被别人使用
- 例子: 开发一个:
- 输入1 返回2
- 输入-1 返回3
- 输入任何其他数,返回1
那么就可以先写unittest当中的case,比如下面代码,不会先去写my_func
之中的内容,而是先写规划,指标, 最后再写开发功能
import unittest
def my_func(a):
return None
class TestFunc(unittest.TestCase):
def test_func(self):
self.assertEqual(my_func(1), 2)
self.assertEqual(my_func(-1), 3)
for i in range(-100, 100):
if i == 1 or i == -1:
continue
self.assertEqual(my_func(i), 1)
unittest.main()
另外一个规定就是,my_func()
通常是写在另一个python文件中的,比如all_my_func.py
,而测试文件test.py
会将all_my_func.py
的my_func()
引入进来做测试的,这样测试和原本的功能就不会混杂在一起了
而且还可以测试多个功能,如果某一个出问题,它也会单独指出
import unittest
def my_func(a):
if a == 1:
return 2
elif a == -1:
return 3
else:
return 1
def my_func2(b):
if b != "yes":
raise ValueError("you can only say yes!")
else:
return True
class TestFunc(unittest.TestCase):
def test_func(self):
self.assertEqual(my_func(1), 2)
self.assertEqual(my_func(-1), 3)
for i in range(-100, 100):
if i == 1 or i == -1:
continue
self.assertEqual(my_func(i), 1)
def test_func2(self):
self.assertTrue(my_func2("yes"))
with self.assertRaises(ValueError):
my_func2("no no no no")
unittest.main()
..
----------------------------------------------------------------------
Ran 2 tests in 0.000s
OK
3)用Python命令执行测试
python -m unittest tests.py
4)能测哪些assert
assert | 含义 |
---|---|
assertEqual(a, b) | a == b |
assertNotEqual(a, b) | a != b |
assertTrue(condition) | condition 是不是 True |
assertFalse(condition) | condition 是不是 False |
assertGreater(a, b) | a > b |
assertGreaterThan(a, b) | a >= b |
assertLess(a, b) | a < b |
assertLessEqual(a, b) | a <= b |
assertIs(a, b) | a is b ,a 和 b 是不是同一对象 |
assertIsNot(a, b) | a is not b ,a 和 b 是不是不同对象 |
assertIsNone(a) | a is None ,a 是不是 None |
assertIsNotNone(a) | a is not None ,a 不是 None? |
assertIn(a, b) | a in b , a 在 b 里面? |
assertNotIn(a, b) | a not in b ,a 不在 b 里? |
assertRaises(err) | 通常和 with 一起用,判断 with 里的功能是否会报错(上面练习有用到过) |
5)测单独的功能
如果写了很多test,但只想测其中的某几个,可以将代码最下面的unittest.main()
替换成单独的测试函数,但这样写也有弊端,因为会需要经常改代码,因为下一次你可能就想测另外的功能了,不灵活
import unittest
def my_func(a):
if a == 1:
return 2
elif a == -1:
return 3
else:
return 1
def my_func2(b):
if b != "yes":
raise ValueError("you can only say yes!")
else:
return True
class TestFunc(unittest.TestCase):
def test_func1(self):
self.assertEqual(my_func(1), 2)
self.assertEqual(my_func(-1), 3)
for i in range(-100, 100):
if i == 1 or i == -1:
continue
self.assertEqual(my_func(i), 1)
def test_func2(self):
self.assertTrue(my_func2("yes"))
with self.assertRaises(ValueError):
my_func2("no no no no")
suite = unittest.TestSuite()
suite.addTest(TestFunc('test_func1'))
unittest.TextTestRunner().run(suite)
.
----------------------------------------------------------------------
Ran 1 test in 0.000s
OK
参考:莫烦Python