异常处理
处理机制
python异常处理机制:
try:
...
except:
...
finally:
...
except可以有多个,根据try块中的异常类型执行不同的except处理,不管有无异常,finally中的代码都会执行。
错误捕获
Python的错误也是class,所有的错误类型都继承BaseException,如果前一个except的错误时下一个except错误的父类,则子类错误中的代码块不执行,如:
try:
....
except ValueError as e:
print('ValueError')
except UnicodeError as e:
print('UnicodeError')
因为UnicodeError是ValueError的子类,故永远不会执行
print('UnicodeError')
使用else
如果没有捕获到相关错误,又需要打印一个log,或需要其他处理,可以使用else,如:
try:
print('try...')
r=10/2
print('result:',r)
except ZeroDivisionError as e:
print('except',e)
else:
print('No error...')
finally:
print('finally...')
print('END')
调用栈
python抛出的异常与Java类似,若没有被捕获到,会一层一层往上抛,最后被python解释器捕获,Java中的异常如果一直没有被捕获,会抛到main函数中
logging模块
python中的logging模块是用来记录错误信息的,打印出错误类型后,还会将错误信息记录到日志中,方便排查,如:
#logging.py
import logging
try:
print('try...')
r=10/0
print('result:',r)
except ZeroDivisionError as e:
logging.exception(e)
else:
print('else ...')
finally:
print('finally...')
print('END')
使用raise
raise语句将错误抛出去,给上一层处理,如:
#logging.py
import logging
try:
print('try...')
r=10/0
print('result:',r)
except ZeroDivisionError as e:
logging.exception(e)
raise
else:
print('else ...')
finally:
print('finally...')
print('END')
调试
调试的方法有:使用print打印,assert,logging,pdb,IDE
print 将错误信息打印
assert 用print查看的地方都可以用assert,其形式为:
assert 表达式,打印的错误信息
若表达式为true,则没有错误,若为false则打印错误信息
若断言失败,assert语句则抛出AssertionError
logging 将错误打印并记录到文件
pdb可以使用python -m pdb 文件名.py来调试代码,可以单步执行,使用n进入下一步(next),也可以使用断点执行,使用c进入下一个断点(continue)
文件test.py内容为:
# err.py
s = '0'
n = int(s)
print(10 / n)
单步调试
执行:python - m pdb test.py
调试如下
C:\PythonCode>python -m pdb test.py
> c:\pythoncode\test.py(2)<module>()
-> s='0'
(Pdb) n
> c:\pythoncode\test.py(3)<module>()
-> n=int(s)
(Pdb) n
> c:\pythoncode\test.py(4)<module>()
-> print(10/n)
(Pdb) n
ZeroDivisionError: division by zero
> c:\pythoncode\test.py(4)<module>()
-> print(10/n)
断点调试:
先在代码中加入断点:pdb.set_trace()
文件如:
#error.py
s='0'
n=int(s)
pdb.set_trace()
print(10/n)
调试结果:
C:\PythonCode>python -m pdb test.py
> c:\pythoncode\test.py(2)<module>()
-> s='0'
(Pdb) c
Traceback (most recent call last):
File "C:\applications\Python\Python36\lib\pdb.py", line 1667, in main
pdb._runscript(mainpyfile)
File "C:\applications\Python\Python36\lib\pdb.py", line 1548, in _runscript
self.run(statement)
File "C:\applications\Python\Python36\lib\bdb.py", line 434, in run
exec(cmd, globals, locals)
File "<string>", line 1, in <module>
File "c:\pythoncode\test.py", line 2, in <module>
s='0'
NameError: name 'pdb' is not defined
Uncaught exception. Entering post mortem debugging
Running 'cont' or 'step' will restart the program
> c:\pythoncode\test.py(2)<module>()
-> s='0'
(Pdb)
IDE
使用PyCharm,VS Code调试
单元测试
对于单元测试,Python自带unittest模块
测试类中,默认以test开头的方法就是测试方法,如test_Method, 不以test开头的方法是不会被执行的
assertEqual:判断输出的是否与我们期望的相等,如:
self.assertEqual(3-2, 1) #判断3-2是否等于1
assertTrue 判断条件是否成立
assertRaises 期待抛出指定类型的Error,如:
#通过d.empty访问不存在的key时,我们期待抛出AttributeError
with self.assertRaises(AttributeError):
value = d.empty
**测试实例 **
student.py如下:
import unittest
class Student(object):
def __init__(self,name,score):
self.name=name
self.score=score
def get_grade(self):
if self.score>100 or self.score<0:
raise ValueError
if self.score>=60 and self.score<80:
print('name:',self.name,'******score:',self.score)
return 'B'
if self.score>=80:
print('name:',self.name,'******score:',self.score)
return 'A'
print('name:',self.name,'******score:',self.score)
return 'C'
testStudent.py如下:
from student import Student
import unittest
class TestStudent(unittest.TestCase):
def test_80_to_100(self):
print('test_80_to_100')
s1=Student('Bart',80)
s2=Student('Lisa',100)
print('s1成绩',s1.get_grade())
self.assertEqual(s1.get_grade(),'A')
print('s2成绩',s2.get_grade())
self.assertEqual(s2.get_grade(),'A')
def test_60_to_80(self):
print('test_60_to_80')
s1=Student('Bart',69)
s2=Student('Lisa',79)
self.assertEqual(s1.get_grade(), 'B')
self.assertEqual(s2.get_grade(), 'B')
def test_0_to_60(self):
print('test_0_to_60')
s1 = Student('Bart', 0)
s2 = Student('Lisa', 59)
self.assertEqual(s1.get_grade(), 'C')
self.assertEqual(s2.get_grade(), 'C')
def test_invalid(self):
print('test_invalid')
s1 = Student('Bart', -1)
s2 = Student('Lisa', 101)
with self.assertRaises(ValueError):
s1.get_grade()
with self.assertRaises(ValueError):
s2.get_grade()
if __name__ == '__main__':
unittest.main()
输出结果:
test_0_to_60
name: Bart ******score: 0
name: Lisa ******score: 59
.test_60_to_80
name: Bart ******score: 69
name: Lisa ******score: 79
.test_80_to_100
name: Bart ******score: 80
s1成绩 A
name: Bart ******score: 80
name: Lisa ******score: 100
s2成绩 A
name: Lisa ******score: 100
.test_invalid
.
----------------------------------------------------------------------
Ran 4 tests in 0.227s
OK
>>>
引号问题
单引号,双引号
单引号和双引号都可以标识字符串,如:
str='python'
strCopy="python"
str和strCopy输出都是python
如果用单引号标识字符串,字符串包含单引号,就必须用转义字符\ ,如:
str='I\'m python'
打印结果:
I'm python
如字符串含多个单引号,看起来特别费劲,因此python为了支持单引号与双引号
若用双引号表示字符串,则字符串里面的单引号为普通字符,不需要转义
若用单引号表示字符串,则字符串里面的双引号为普通字符,不需要转义
如:
str="I'm python, and PHP is the best language,isn't it???"
print(str)
输出
I'm python, and PHP is the best language,isn't it???
>>>
3个单引号与3个双引号
当字符串很长,需要写多行,且要单行打印时,可考虑使用3个单引号或3个双引号
字符串有单引号即可用双引号定义字符串,反之用单引号定义字符串
如:
str="I am python\
He is C++\
She is PHP"
print(str)
输出:
I am pythonHe is C++She is PHP
>>>
使用3个双引号
str="""I am python
He is C++
She is PHP
"""
print(str)
输出:
I am python
He is C++
She is PHP
使用3个引号得另一个作用:可直接添加注释,如:
str="""I am python #python大法好
He is C++ #我比你们快
She is PHP #呵呵,PHP才是最好的语言
"""
print(str)
输出:
I am python #python大法好
He is C++ #我比你们快
She is PHP #呵呵,PHP才是最好的语言
>>>