1. 概念区分
- 错误:
是由于逻辑或语法导致一个程序无法正常运行的问题, 错误的特点是有些错误是无法预知的
- 异常:
通知上层调用者,有错误产生需要处理, 用作信号通知
是程序出错时标示的一种状态, 当异常发生时,程序不会向下继续执行,而转去调用此函数的地方,待处理此错误
- python3 中常用的错误类型:
ZeroDivisionError 除(或取模)零
StopIteration 迭代器没有更多的值
IOError 输入输出操作失败
ImportError 导入模块失败
GeneratorExit 生成器发生异常通知退出
IndexError 序列中没有此索引
IndentationError 缩紧错误
ValueError 传入无效的参数
AssertionError 断言语句失败
NameError 未声明/初始化对象
AttributeError 对象没有
详见:>>> help(__builtins__)
2. 异常捕获
捕获异常,将程序转为正常流程
- try-except语句语法:
try:
可能触发异常的语句
except 错误类型1 [as 变量1]:
异常处理语句1, 根据错误类型进行匹配,如果匹配成功,则程序转入正常状态,并调用处理语句进行处理
except 错误类型2 [as 变量2]:
异常处理语句2
...
except:
如果没有相应的错误类型与之匹配,则执行空类型的except子句(except:),之后程序进入正常状态; 如果没有该子句并且没有匹配到任何错误类型,则程序的异常状态会继续向上(调用处)传递,至入传入解释执行器后终止程序执行
else:
未发生异常执行语句
finally:
最终执行语句(一定会被执行)
说明:as 部分可以省略
except 子句可以有一个或多个,但至少要有一个
else 子句最多只能有一个,也可以省略不写
finally 子句最多只能有一个,也可以省略不写
- try-finally 语句语法:
try-finally语句不会改变程序的状态(正常/异常)状态, 用来做触发异常时必须处理的事情,无论异常是否发生,finally子句都会被执行; 通常try/finally 语句的finally 子句不可以省略,一定不存在except 子句;
try:
可能触发异常的语句
finally:
最终语句
def fry_egg():
print("打开天然气!")
print("开始煎鸡蛋!")
try:
n = int(input("请输入蛋的个数")) # 可触发异常
print("完成煎蛋!共煎了%d个鸡蛋" % n)
finally:
print("关闭天然气!")
fry_egg()
打开天然气!
开始煎鸡蛋!
请输入蛋的个数a
关闭天然气!
---------------------------------------------------------------------------
ValueError Traceback (most recent call last)
<ipython-input-5-426dac8668b5> in <module>
8 print("关闭天然气!")
9
---> 10 fry_egg()
<ipython-input-5-426dac8668b5> in fry_egg()
3 print("开始煎鸡蛋!")
4 try:
----> 5 n = int(input("请输入蛋的个数")) # 可触发异常
6 print("完成煎蛋!共煎了%d个鸡蛋" % n)
7 finally:
ValueError: invalid literal for int() with base 10: 'a'
3. raise 语句
触发一个错误,让程序进入异常状态
- 语法
raise 异常类型 或 异常对象
def make_raise():
print("start")
raise ZeroDivisionError("假的零除")
print("end")
try:
make_raise()
except ZeroDivisionError as err: # err绑定错误对象
print("出现错误")
print("错误类型:", err)
start
出现错误
错误类型: 假的零除
4. assert 语句(断言语句)
当真值表达式为False时, 用错误数据创建一个AssertionError类型的错误,并进入异常状态
- 语法
assert 真值表达式, 错误数据(通常是字符串)
等同于:
if 真值表达式 == False:
raise AssertionError(错误数据)
def get_age():
a = input("请输入年龄:")
a = int(a) # ValueError错误
assert a < 150, "年龄太大了"
assert a > 0, "还没出生"
return a
while True: # 循环读入解决NameValue错误
try:
age = get_age() or 0
break
except AssertionError as e:
print("发生年龄值不合法的错误,", e)
except ValueError as e:
print("发生输入值不能转为整数的错误", e)
print("您输入的年龄是:", age)
请输入年龄:a
发生输入值不能转为整数的错误 invalid literal for int() with base 10: 'a'
请输入年龄:1111
发生年龄值不合法的错误, 年龄太大了
请输入年龄:2
您输入的年龄是: 2
练习: 练习:写一个函数getscore() 获取学生输入的成绩(0~100),如果输入出现异常,则用 try-except语句来处理异常并此函数返回0,如果一切正常则返回输入的成绩
def get_score():
score = int(input("输入学生成绩:"))
if score < 0:
raise ValueError("成绩小于零")
if score > 100:
raise ValueError("成绩大于零")
return score
while True:
try:
score = get_score()
break
except ValueError as err:
print("有错误发生:", err)
print("学生的成绩是:", score)
输入学生成绩:-1
有错误发生: 成绩小于零
输入学生成绩:111
有错误发生: 成绩大于零
输入学生成绩:a
有错误发生: invalid literal for int() with base 10: 'a'
输入学生成绩:12
学生的成绩是: 12
4. python 调试器 pdb模块
pdb是python debugger 调试器. 既可以在命令行执行python时进行调试,也可以作为模块被调用.
- 运行python使用:
python -m pdb mysqcript.py 则会进入调试模式
(Pdb) # 会自动停在第一行,等待调试
- 调试命令
1. 功能
h (help) # 帮助, 列出可用命令, h command 可用查看具体命令
q (quit) # 退出
!statement # 在目前的环境中执行statement (就是执行指定python语句)。
alias name command # 以一个”别名”代替”一群出错命令”,有点类似c/c++ 的 macro。可在.pdbrc文件内定义..
unalias name # 取消某个 alias定义的别名。
run # 重新运行脚本. 后面可以跟参数, 会覆盖原来sys.argv保存的参数. 2.6后新加.
2. 断点设置
b lineno (break) # 断点设置在本py的第 lineno 行, 或 b ots.py:20 #断点设置到 ots.py第20行. b funcname #断点设置到指定函数处; b # 查看所有断点编号及信息
tbreak lineno # 临时断点, 用法同break, 但只能断一次.
cl num (clear) # 删除第 num 个断点, num由b列出的Num列决定.如果没有带参数,则清除所有断点。也可指定行号.
condition bpnumber [condition] # 设置断点条件, 不写条件则会取消条件设置.
disable bpnumber # 取消指定断点的功能,但仍然保留断点可以重新激活。不指定 bpnumber会失效暂停所有断点.
enable bpnumber # 恢复断点的功能。
ignore bpnumber count # 设定断点的忽略次数。如果没指定count,其初始为0。当count为0时,断点会正常动作。若有指定 count,则每次执行到该中断,count就少1,直到count数为0。
commands bpnumber # 设置断点号处执行指定命令.使用该命令后进入cmd模式,然后可以输入相关要执行的命令, 输入end结束. 直接输入end会取消相应号的命令. 2.5版新加
3. 运行
c (continue) # 继续运行直到下个断点
n (next) # 单步运行, 不会进入函数内部而是直接到下一行.
s (step) # 单步运行, 但会进入到调用函数的内部.
r (return) # 继续执行直到函数返回处(return的地方).
j lineNo (jump) # 跳到某行执行,可以前跳或后跳。不是所有行都可跳. 只有在 call stack 的最底部才能作用。
unt (until) # 继续执行直到行号大于当前行. ?? 2.6后新加.
4. 查看
l (list) # 列出目前所在档案中的位置。->所指是现在所在代码(一般是可执行代码,默认上下5行). 连续地 l 命令会一直列到档案结尾。指定一个数字参数 n, 就是列出该行上下文; 两个参数n,m, 就是一个范围(n<m) 或指定行及以后的m行.
p var (print) # 查看变量 var 当前值
pp 和 p 命令类似,但是使用 pprint module。
a (args) # 查看全部栈内当前函数的参数变量
w (where) 列出目前call stack 中的所在层。(调用栈中的处理)
d (down) 在call stack中往下移一层 (较新).
u (up) 在call stack中往上移一层(较旧)。如果在上移一层之后按下 n ,则会在上移之后的一层执行下一个叙述,之前的 function call 就自动返回。
- 在python中使用pdb模块可以进行调试.
import pdb
pdb.set_trace() : 设置断点, 在运行脚本到此时进入pdb模式, 指令则指到下一行.
pdb.run(statement, globals=None, locals=None): 主要用于调试指定内容语句块.
statement: 要调试的语句块,以字符串的形式表示
globals: 可选参数,设置statement运行的全局环境变量
locals: 可选参数,设置statement运行的局部环境变量
pdb.runeval(statement, globals=None, locals=None): 和run类似, 但会对statement字符串先进行运算
pdb.runcall(funcname, *args, **kwds): 调试指定函数.后面可以指定传递的函数参数. 多参数要用,隔开.
pdb.post_mortem([traceback]): post-mortem调试模式处理traceback或新近的错.
pdb.pm(): post-mortem调试模式处理sys.last_traceback 的反馈
同名公众号: 庄AC