python 基础知识整理学习
错误处理、调试与测试
提前捕获并处理代码中可能出现的错误,使代码更健壮。
错误处理
代码中报错会中断程序,不会继续执行。
# 打印输出未定义的变量,报错后不会输出后面的数字
print(a)
print(2)
输出错误:
try…except…finally
错误捕获,记录错误,方便查询
try:
print(a)
except NameError as e:
print("变量未声明,请检查")
finally:
print("继续执行!")
print(2)
说明:
- 刚才的错误类型为
NameError
,使用except
语句捕获并处理; - 所有的错误类型都继承
BaseException
基类,错误捕获得从子类到父类,层层向上捕获。传递门 - 错误会被层层传递,主要在于函数的调用,在哪调用错误会先从哪抛出,一直抛给系统,程序终止。
logging记录
通过logging
可以把错误记录到错误日志中,方便日后查看。也可以打印出来,程序不会被终止。
# logging 记录日志
import logging
try:
print(10/0)
except ZeroDivisionError as e:
logging.exception(e)
print(0/10)
说明:
logging
类型记录包括:debug
/error
/info
/warning
- 默认的
logging.info()
是不会输出的。通过配置logging.basicConfig(level=logging.INFO)
自定义错误
系统内置的错误类型不能满足工作,还可以自定义错误类型,并抛出。
# 自定义错误类型
class ConflictValueError(ValueError):
pass
name = input("please input your name \n")
try:
if name == "admin":
raise ConflictValueError("you can't use this name")
except ConflictValueError as e:
logging.exception(e)
print("ok")
说明:
- 尽可能使用系统内置的错误类型,自定义类型继承系统的某一个类型错误
BaseException
。 - 错误捕获处理一定是有助于问题排查。
调试
功能测试,问题排查。
print()
打印结果输出,标记;- 断言
assert
,断言失败会抛出AssertionError
错误
说明:# 断言 assert name!="test","测试" print("大神,你好")
- 断言抛出错误后,程序终止执行
- 通过执行时加入参数
python -O four.py
忽略断言。
logging
日志输出记录。- 调试器
pdb
python -m pdb **.py
启动调试,按步执行。
说明:l
查看代码,会显示当前执行位置。n
执行下一步。p 变量名
来查看已执行的语句中的变量的值。q
结束退出。c
程序开始正常执行。
pdb.set_trace()
设置断点,程序执行到这停止。
需要导入import pdb
单元测试
编写一个简单的用户名校验类,自定义各种错误类型,放置在单独的文件four_form.py
:
# 自定义表单校验错误类
class ConflictValueError(ValueError):
pass
class ExistError(ValueError):
pass
class PureNumberError(ValueError):
pass
class LenError(ValueError):
pass
# 表单用户名校验 、 不能是纯数字、不能有特殊符号、必须6位以上
import re
class ValidForm():
def __init__(self,**ot):
self.form = ot
def exist_name(self):
form = self.form
if "name" in form:
pass
else:
raise ExistError("必须填写用户名!")
def conflict_name(self):
form = self.form
self.exist_name()
if form["name"] in ["admin","test","login"]:
raise ConflictValueError("你不能使用这个名字")
def pureNumber_name(self):
form = self.form
self.conflict_name()
if re.search(r'^\d+$',str(form["name"]))!=None:
raise PureNumberError("不能是纯数字!")
def len_name(self):
form = self.form
self.pureNumber_name()
if len(form["name"]) <6:
raise LenError("用户名不少于6个字符!")
def isAvailable(self):
self.len_name()
return True
# 测试方法
# ValidForm(**{"name":"232"}).isAvailable()
编写测试函数,单独的文件four_test.py
:
import unittest
from four import ValidForm,ExistError,ConflictValueError,LenError
class TestValidForm(unittest.TestCase):
# 在每个测试方法开始前调用 before
def setUp(self):
print("start")
def test_existError(self):
with self.assertRaises(ExistError):
ValidForm().exist_name()
def test_conflicError(self):
with self.assertRaises(ConflictValueError):
ValidForm(**{"name":"admin"}).conflict_name()
@unittest.skip("将不会被调用")
def test_lenError(self):
with self.assertRaises(LenError):
ValidForm(**{"name":"abc"}).len_name()
# 方法调用后执行
def tearDown(self):
print("end")
if __name__ == '__main__':
unittest.main()
说明:
- 依赖模块
unittest
,需导入; - 不同文件中,需要调用用到的模块
from ... import **
unittest.main()
提供了在命令行运行测试脚本的接口,直接运行python four_test.py
setUp()
每个测试方法执行前都会去调用;tearDown()
测试方法执行完后调用unittest
内部提供了断言的方法,如assertRaises()
/assertEqual()
/assertTrue()
,- 注解可以改变方法的表现,比如
@unittest.skip()
使得这个测试方法不会被调用;
文档测试
以注释的方式写测试用例,然后指定执行注释中可执行的代码:
import re
class ValidForm():
'''
valid name
>>> ValidForm().exist_name()
Traceback (most recent call last):
...
ExistError: 必须填写用户名!
>>> ValidForm(**{"name":"admin"}).conflict_name()
Traceback (most recent call last):
...
ConflictValueError: 你不能使用这个名字
>>> ValidForm(**{"name":"admin_123"}).isAvailable()
True
'''
def __init__(self,**ot):
self.form = ot
# 省略.....
if __name__ == "__main__":
import doctest
doctest.testmod()
说明:
>>> **
必须有空格,才会当做可执行命令执行;- 期望一定要和执行的结果一致;不一致时会报错
Expected: ... Got: ...
- 当符合期望时,执行没有任何输出。改错一处
ValidForm(**{"name":"ad_min"}).conflict_name()
执行结果和期待的不一致:
doctest
只会在命令行模式下执行。>>>
标识决定了执行环境。