池州市网站建设_网站建设公司_Spring_seo优化
2025/12/18 0:52:46 网站建设 项目流程

Python学习全部目录:
【Python学习】基础学习(一):变量与运算、数据类型、函数、类、模块
【Python学习】基础学习(二):文件管理与I/O编程

【Python学习】基础学习(三):异常处理、调试和测试

    • 异常处理、调试和测试
      • 控制异常try-except
      • 调试器pdb
      • Unittest单元测试
      • 异常/警告名称列表

异常处理、调试和测试

控制异常try-except

即使自己的的代码没有错误,但是有时也难以避免潜在的错误,比如说物理环境出错,CPU资源不够,调用的服务失败。
在写代码的时候,考虑如何对捕捉潜在的错误,预先写好对于可能错误的处理。

  1. try-except
    比如处理文件时候,如果读写不存在的文件就会报错:
withopen('no_file.txt','r')asf:print(f.read())# FileNotFoundError: [Errno 2] No such file or directory: 'no_file.txt'

我们需要注意的就是表示异常类型的报错关键词FileNotFoundError,需要手动捕捉这个错误并且进行处理

try:withopen('no_file.txt','r')asf:print(f.read())exceptFileNotFoundErrorase:print(e)withopen('no_file.txt','w')asf:f.write('This is a file for test.')print('new file "no_file.txt" has been written.')

有时候程序执行某种功能会报多种不同的异常,如果异常的处理方案相同,可以在except中写多个异常种类,会按照正常的执行顺序, 一次检测异常,报出第一个遇到的异常:

dic={'name':'Bob','age':20}l=[1,2,3]try:male=dic['gender']l[3]=4except(KeyErrororIndexError)ase:print('ker or index error for: ',e)# ker or index error for: 'gender'
  1. try-except-except
    如果想让多种异常分开处理,就需要写两个except
try:v=dic['gender']l[3]=4exceptKeyErrorase:print('key error for:',e)dic['gender']='Female'exceptIndexErrorase:print('index error for:',e)l.append(4)print(dic)print(l)# key error for: 'gender'# {'name': 'Bob', 'age': 20, 'gender': 'Female'}# [1, 2, 3]
  1. try-except-else
    这个模式在except处理报错情况:
lis=[1,2,3]try:print(lis[3])exceptIndexErrorase:print(e)lis.append(4)print(lis[3])else:print('now is in else.')# list index out of range# 4

else处理没有报错的情况:

lis=[1,2,3,4]try:print(lis[3])exceptIndexErrorase:print(e)lis.append(4)print(lis[3])else:print('now is in else.')# 4# now is in else.
  1. try-except-finally
    不管有没有错误,都会执行到finally内容
    有报错:
lis=[1,2,3]try:print(lis[3])exceptIndexErrorase:print(e)lis.append(4)print(lis[3])finally:print('reach finally.')# list index out of range# 4# reach finally.

没有报错:

lis=[1,2,3,4]try:print(lis[3])exceptIndexErrorase:print(e)lis.append(4)print(lis[3])finally:print('reach finally.')# 4# reach finally.

主要用在不管有没有报错,都想让程序继续执行,不处理任何异常:

try:dd=ddddfinally:print('I know there is an error, just ignore it.')# I know there is an error, just ignore it.# NameError: name 'dddd' is not defined
  1. 记录错误
    使用python内置的logging模块,就可以记录并打印输出错误信息,同时可以让程序继续执行,而不是出错就退出执行:
importlogging lis=[1,2,3]deflogerror():try:print(lis[3])exceptExceptionase:# Exception是基类logging.exception(e)logerror()print('main')# IndexError: list index out of range# main
  1. raise手动触发异常
    主动抛出异常,在错误的地方中断程序并输出清晰地错误信息
defno_negative(num):ifnum<0:raiseValueError('No Negative!')returnnum no_negative(-1)# Traceback (most recent call last):# ValueError: No Negative!

这种raise的用法,更常用在网络请求、读取数据库时,如果有raise写入到log中就会非常清晰易懂。

总结:做一个小练习

# 运行下面的代码,根据异常信息进行分析,定位出错误源头,并修复:fromfunctoolsimportreducedefstr2num(s):returnint(s)defcalc(exp):ss=exp.split('+')ns=map(str2num,ss)returnreduce(lambdaacc,x:acc+x,ns)defmain():r=calc('100 + 200 + 345')print('100 + 200 + 345 =',r)r=calc('99 + 88 + 7.6')print('99 + 88 + 7.6 =',r)main()# 100 + 200 + 345 = 645# Traceback (most recent call last):# File "/home/bupt/python/python_basic.py", line 17, in <module># main()# File "/home/python/python_basic.py", line 14, in main# r = calc('99 + 88 + 7.6')# File "/home/python/python_basic.py", line 9, in calc# return reduce(lambda acc, x: acc + x, ns)# File "/home/python/python_basic.py", line 4, in str2num# return int(s)# ValueError: invalid literal for int() with base 10: ' 7.6'

从上往下分析错误信息,最后发现是str2num(s)函数调用的int(s)出错,不能转换7.6这个浮点数

调试器pdb

(以下为不依赖IDE的调试方式)

启动python的调试器pdb,让程序以单步方式运行,可以随时查看运行状态:

s='0'n=int(s)print(10/n)

配置参数-m pdb命令行执行python程序:
python -m pdb python_basic.py
使用n单步执行,使用p 变量名查看变量的值

>/home/python/python_basic.py(1)<module>()->s='0'(Pdb)(Pdb)n# 使用 n 单步执行>/home/python/python_basic.py(2)<module>()->n=int(s)(Pdb)n>/home/python/python_basic.py(3)<module>()->print(10/ n)(Pdb)p s# 使用 p 查看变量'0'(Pdb)n ZeroDivisionError: division by zero>/home/python/python_basic.py(3)<module>()->print(10/ n)(Pdb)n --Return--

可以import pdb,使用pdb.trace()设置断点:

importpdb s='0'n=int(s)pdb.set_trace()print(10/n)

使用python python_basic.py运行程序,会自动在断点处暂停进入pdb调试环境,使用命令p查看变量,使用命令c继续运行:

$ python python_basic.py>/home/bupt/python/python_basic.py(5)<module>()->pdb.set_trace()(Pdb)p s'0'(Pdb)p n0(Pdb)c Traceback(most recent call last): File"/home/bupt/python/python_basic.py", line6,in<module>print(10/ n)~~~^~~ ZeroDivisionError: division by zero

Unittest单元测试

运行整套程序,检查哪里出错的测试方法只适合:小型项目/项目功能之间关联少的少量功能项目,否则,使用单元测试是更好的测试方法。

单元测试就是不直接测试全套程序逻辑,而是将小功能模块拆开逐个测试。在Python中,常用unittest测试框架做单元测试。

在class中继承unittest.TestCase,编写函数,以test开头的TestXxx类被认为是测试类,test_xxx方法被认为是测试方法
__main__函数中执行unittest.main(),这个“测试运行器”会自动发现并执行定义的测试类中所有的测试方法:

importunittestclassTestFunc(unittest.TestCase):deftest_example(self):self.assertEqual(2,divide(2,1))self.assertEqual(2,divide(-2,-1))# self.assertEqual(-1, divide(-2, 0))defdivide(a,b):returna/bif__name__=='__main__':unittest.main()# .# --------------------# Ran 1 test in 0.000s# OK

如果测试不通过,会报错:

self.assertEqual(-1,divide(-2,0))# return a / b# ~~^~~# ZeroDivisionError: division by zero# ---------------------------------------------------------------------# Ran 1 test in 0.000s# FAILED (errors=1)
  1. unittest规范
    可以先写unittest,然后再开发功能,类似于先设立目标,然后实现目标:
    比如想要:输入 1 返回 2,输入 -1 返回 3,输入其他任何数,返回 1。可以像下面一样先把test_func部分写好:
classTestFunc(unittest.TestCase):deftest_example(self):# 期望实现的功能self.assertEqual(2,my_func(1))self.assertEqual(3,my_func(-1))foriinrange(-100,100):ifi==1ori==-1:continueself.assertEqual(1,my_func(i))defmy_func(num):if__name__=='__main__':unittest.main()

测试文件要与原本的功能文件分开,测试文件以_test.py结尾,引入from all_my_func import my_func功能模块函数以进行测试,
可以同时测试多个功能模块:

defmy_func1(a):ifa==1:return2elifa==-1:return3else:return1defmy_func2(letter):ifletter=='yes':returnTrueelse:raiseValueError("Only YES!")classTestFunc(unittest.TestCase):deftest_func1(self):self.assertEqual(2,my_func1(1))self.assertEqual(3,my_func1(-1))foriinrange(-100,100):ifi==1ori==-1:continueself.assertEqual(1,my_func1(i))deftest_func2(self):self.assertTrue(my_func2('yes'))withself.assertRaises(ValueError):# 报错才能通过testmy_func2("nononono")if__name__=="__main__":unittest.main()# ..# ----------------------------------------------------------------------# Ran 2 tests in 0.000s# OK

通常将测试代码写在test.py文件中,在命令行使用python指令执行测试:
python -m unittest test.py

如果写出了很多的功能测试,但有时只想执行某一个test,可以进行如下替换:
但是这样写灵活性太差:

# unittest.main()suite=unittest.TestSuite()suite.addTest(TestFunc('test_func1'))unittest.TextTestRunner.run(suite)# .# ----------------------------------------------------------------------# Ran 1 test in 0.000s# OK

不如直接命令行执行对应参数命令来执行不同的test,灵活性更强:
python -m unittest testmodule.testclass.test_method

  1. setUp()&tearDown()
    在使用unittest进行单元测试时,可能需要前置准备后置清理
    -> 测试前需要准备数据,连接数据库,打开文件;
    -> 测试后需要清理数据,关闭数据库连接,删除临时文件
    为了避免在每一个测试方法中重复写这些准备和清理的代码,unittest提供了两个钩子方法:
  • setUp()
    在每个测试方法运行之前都会被自动调用
    用于创建测试所需要的环境或数据准备
  • tearDown()
    在每个测试方法运行之后都是被自动调用
    用于释放资源,清楚测试过程中产生的数据或恢复环境状态
importunittestclassTestExample(unittest.TestCase):defsetUp(self):print('setUp')returnsuper().setUp()deftearDown(self):print('tearDown')returnsuper().tearDown()deftest_fun1(self):print('test func 1')self.assertTrue(True)deftest_fun2(self):print('test func 2')self.assertEqual(1+1,2)if__name__=='__main__':unittest.main()# setUp# test func 1# tearDown# .setUp# test func 2# tearDown# .# ------------------------------------------# Ran 2 tests in 0.000s# OK

数据库连接场景:

defsetUp(self):self.conn=db.connect()self.cursor=self.conn.cursor()deftearDown(self):self.cursor.close()self.conn.close()

临时文件创建与删除:

defsetUp(self):self.file=open('temp.txt','w')deftearDown(self):self.file.close()os.remove('temp.txt')
  1. unittest断言方法列表
断言方法含义
assertEqual(a, b)判断a == b
assertNotEqual(a, b)判断a != b
assertTrue(condition)判断condition 是 True
assertFalse(condition)判断condition 是 False
assertGreater(a, b)判断a > b
assertGreaterEqual(a, b)判断a >= b
assertLess(a, b)判断a < b
assertLessEqual(a, b)判断a <= b
assertIs(a, b)判断a is b(是否是同一个对象
assertIsNot(a, b)判断a is not b(不是同一个对象)
assertIsNone(a)判断a is None
assertIsNotNone(a)判断a is not None
assertIn(a, b)判断a in b(a 是否在 b 中)
assertNotIn(a, b)判断a not in b(a 不在 b 中)
assertRaises(error)判断是否抛出了指定的异常(常与with一起使用)

异常/警告名称列表

异常名称描述
BaseException所有异常的基类
SystemExit解释器请求退出
KeyboardInterrupt用户中断执行(通常是 Ctrl+C)
Exception常规错误的基类
StopIteration迭代器没有更多的值
GeneratorExit生成器(generator)发生异常来通知退出
StandardError(Python 3 中已移除)所有内建标准异常的基类
ArithmeticError所有数值计算错误的基类
FloatingPointError浮点计算错误
OverflowError数值运算超出最大限制
ZeroDivisionError除以零或取模为零(适用于所有数据类型)
AssertionError断言语句失败
AttributeError对象没有该属性
EOFError没有内建输入,到达 EOF 标记
EnvironmentError(已合并进 OSError)操作系统错误的基类
IOError(Python 3中与 OSError 合并)输入/输出操作失败
OSError操作系统错误
WindowsError(仅限 Windows,Python 3 已移除)系统调用失败
ImportError导入模块/对象失败
LookupError无效数据查询的基类
IndexError序列中没有此索引
KeyError映射中没有这个键
MemoryError内存溢出错误(不是致命错误)
NameError未声明或未初始化的对象(变量不存在)
UnboundLocalError访问未初始化的本地变量
ReferenceError弱引用(weakref)试图访问已被回收的对象
RuntimeError一般的运行时错误
NotImplementedError尚未实现的方法
SyntaxErrorPython 语法错误
IndentationError缩进错误
TabErrorTab 与空格混用导致缩进错误
SystemError解释器系统错误
TypeError对类型无效的操作
ValueError传入无效参数(类型对但值不对)
UnicodeErrorUnicode 相关错误的基类
UnicodeDecodeErrorUnicode 解码错误
UnicodeEncodeErrorUnicode 编码错误
UnicodeTranslateErrorUnicode 转换错误
警告名称描述
Warning警告的基类
DeprecationWarning关于被弃用特性的警告
PendingDeprecationWarning将来某个版本会弃用的特性警告
FutureWarning构造将来语义可能会改变的警告
RuntimeWarning可疑的运行时行为警告
SyntaxWarning可疑的语法警告
UserWarning用户代码生成的警告
OverflowWarning(Python 2 使用,Python 3 已移除)自动转为 long 类型的旧警告

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询