错误,异常和程序调试

7.错误,异常和调试

        在Python中,常见的基本错误有两类,及语法错误和异常.对于语法错误,应该在程序编写过程中尽量予以避免,在程序调试中消除.而异常时Python程序在运行过程中引发的错误,如果程序中引发了未进行处理的异常,程序就会由于异常而终止运行,只有为程序添加异常处理,才能使程序更'健壮'.

        Python对异常的处理,有它自己的语法形式.通过本章的学习,可以掌握如何在Python中处理异常和进行程序调试,本章主要内容有:

  • 语法错误
  • 异常的概念
  • 用try语句捕获异常
  • 常见异常的处理
  • 自定义异常
  • 使用pdb调试Python程序

7.1 语法错误

        语法错误是多有编程语言中都存在的一种常见错误,即程序的写法不符合编程语言的规定.常见的语法错误有:

   1.拼写错误

        即Python语言中的关键字写错.变量名,函数名拼写错误等.

        关键字拼写错误时会提示SyntaxError(语法错误),而变量名,函数名拼写错误会在运行时给出NameError的错误提示,例如:

[实例7.1]演示了一个语法上的错误,这里写错了一个单词,代码如下:

for i in range(3):
    printt(i)

[代码说明]实例中代码的第二行中的函数名print被错写成了printt.

[运行效果]会出现NameError错误提示,并同时指出错误所在的行等,如图:

Traceback (most recent call last):
  File "D:/PycharmProjects/untitled/YangQing/异常和日志.py", line 4, in <module>
    printt(i)
NameError: name 'printt' is not defined

Process finished with exit code 1

  2.脚本程序不符合Python的语法规范

     例如少了括号,冒号等符号,以及表达式书写错误等.

  3.缩进错误

    因为Python语法规定,以缩进作为程序的语法之一,这应该是Python语言独特的一面.一般来说Python标准的缩进是以4个空格作为一个缩进.当然,你可以依据自己的习惯,使用Tab也可以.但同一个程序或项目应该统一使用同一种缩进风格.

7.2 异常的处理

异常时Python程序在运行过程中引发的错误.如果程序中引发了未进行处理的异常,脚本就会因为异常而终止运行.只有在程序中捕获这些异常,并进行相关的处理,才能是程序不会中断运行.

7.2.1 异常处理的基本语法

    Python中使用try语句来处理异常,和Python中其他语句一样,try语句也要使用缩进结构,try语句也有一个可选的else语句块.一般的try语句基本形式如下:

       try:

        <语句(块)>                 #可能产生异常的语句(块)

    except<异常名1>:          #要处理的异常

         <语句(块)>                 #异常处理语句

    

    except<异常名2>:          #要处理的异常

         <语句(块)>                 #异常处理语句

     ......

    else:

          <语句(块)>                  #为触发异常,则执行该语句块

    finally:

           <语句块>                              #始终执行该语句,一般为了达到释放资源等目的注意:else语句块在未引发异常情况下得到运行.

在实际应用中可以根据程序的需要而使用部分语句,常见的形式有:

形式一:

    try:

        <语句块>

    except<异常名1>:

        <语句块>

[实例7-2]演示了对于没运行时异常的程序,不论是否捕获异常,程序都会正常运行,但是如果程序中发生了运行时异常,被捕获则程序运行不会中断,否则程序运行会中断退出.

#usr/bin/python
#-*-coding:utf-8-*-
# for i in range(3):
#     printt(i)
def testTry(index,flag=False):
    stulst = ["John","Jenny","Tom"]
    if flag:                                                      #flag为True时.捕获异常
        try:
            astu = stulst[index]
        except IndexError:
            print("IndexError")
        return "Try Test Finished!"
    else:                                                          #flag为False时.不捕获异常

        astu = stulst[index]
        return "No Try Test Finished!"
print("Right params testing start...")
print(testTry(1,True))                                             #不越界参数,捕获异常
print(testTry(1,False))                                            #不越界参数,不捕获异常
print("Error params testing start...") 
print(testTry(4,True))                                             #越界参数,捕获异常
print(testTry(4,False))                                            #越界参数,不捕获异常

[代码说明]本例定义了一个用于测试捕获异常的函数testTry,flag为True是,函数testTry运行时捕获异常,反之,该函数运行时不捕获异常,当传入的index参数正确时(不越界),测试结果都是正常运行的,当传入index错误(越界)时,如果不捕获异常,则程序运行中断.

[运行效果]如图所示

Traceback (most recent call last):
Right params testing start...
  File "D:/PycharmProjects/untitled/YangQing/异常和日志.py", line 21, in <module>
Try Test Finished!
    print(testTry(4,False))
No Try Test Finished!
Error params testing start...
  File "D:/PycharmProjects/untitled/YangQing/异常和日志.py", line 14, in testTry
IndexError
    astu = stulst[index]
Try Test Finished!
IndexError: list index out of range

Process finished with exit code 1

形式二:

try:

    <语句块>

 except <异常名1>:

    <语句块>

finally:

    <语句块>

[实例7-3]演示运用finally语句块来确保文件使用后能关闭该文件,代码如下:        

def testTryFinally(index):
    stulst = ["Jhon","Jenny","Tom"]
    af = open("my.txt","wt+")
    try:
        af.write(stulst[index])
    except:
        pass
    finally:
        af.close()                              #无论是否产生越界异常,都关闭文件
        print("File already had been closed!")
print("No IndexError...")
testTryFinally(1)                               #无越界异常,关闭文件
print("IndexError...")
testTryFinally(4)                               #有越界异常,关闭文件

[代码说明]本实例定义了一个测试函数testTryFinally,在异常捕获代码中加入了finally代码块,其中的代码块是用来关闭文件,并输出一行信息,无论传入的index参数值是否导致发生运行时异常(越界),总是能正常关闭打开的my.txt文件.}

[运行效果]程序代码运行的结果,如图所示.

No IndexError...
File already had been closed!
IndexError...
File already had been closed!

Process finished with exit code 0

7.2.2    Python主要的内置异常及其处理

在Python中常见的异常都已经预定义好了,在交互环境中,用dir(__builtins__)命令,就会显示出所有的预定义异常,常见的预定义异常如下表所示.

  • AttributeError                           调用不存在的方法引发的异常
  • EOFError                                    遇到文件末尾引发的异常
  • ImportError                                导入模块出错引发的异常
  • IndexError                                  列表越界引发的异常
  • IOError                                      I/O操作引发的异常,如打开文件出错等
  • KeyError                                    使用字典中不存在的关键字引发的异常
  • NameError                                使用不存在的变量名引发的异常
  • TabError                                    语句块缩进不正确引发的异常
  • ValueError                                搜索列表中不存在的值引发的异常
  • ZeroDivisionError                     除数为零引发的异常

except语句主要有以下几种用法:

  • except: # 捕获所有异常
  • except<异常名>:#捕获指定异常
  • except(异常名1,异常名2):#捕获异常名1或者异常名2
  • except<异常名>as<数据>:捕获指定异常及其附加的数据
  • except(异常名1,异常名2)as<数据>:#捕获异常名1或者异常名2及其异常的附加数据

[实例7-4]演示了程序中若捕获所有异常,则无论运行发生了什么异常,程序都不会中断

def testTryAll(index,i):
    stulst = ["John","Jenny","Tom"]
    try:
        print(len(stulst[index])/i)
    except:                                          #捕获所有异常
        print("Error!")

print('Try all...Right')
testTryAll(1,2)                                       #正常输出结果
print('Try all...one Error')
testTryAll(1,0)                                     #发生除0异常
print('Try all...two Error')
testTryAll(4,0)                                     #越界异常和除0异常同时发生

[代码说明]代码中定义了函数testTryAll,try语句中捕获了所有的异常,第三次测试中,虽然同是发生了越界和除0异常,但程序不会中断,因为try语句中的except捕获了所有异常.

[运行效果]

Try all...Right
2.5
Try all...one Error
Error!
Try all...two Error
Error!

[实例7-5]演示了程序中捕获了部分异常,当程序运行时引发了不能被捕获的异常时,仍然会中断.

def testTryOne(index,i):
    stulst = ["John","Jenny","Tom"]
    try:
        print(len(stulst[index])/i)
    except IndexError:
        print("Error!")

print('Try all...Right')
testTryOne(1,2)                                       #正常输出结果
print('Try all...one Error')
testTryOne(4.2)                                     #发生越界异常时被捕获,程序不会中断
print('Try all...two Error')
testTryOne(1,0)                                     #发生除0异常,未捕获,程序中断,有错误提示

[代码说明]到吗中定义了函数testTryOne,其Try语句中只捕获了指明的IndexError异常,因此,当程序引发了IndexError异常时,程序不会中断.而当程序引发了除0异常时,程序会中断运行.

[运行效果]

Traceback (most recent call last):
Try all...Right
2.5
Try all...one Error
  File "D:/PycharmProjects/untitled/YangQing/异常和日志.py", line 67, in <module>
    testTryOne(4.2)                                     #发生越界异常时被捕获,程序不会中断
TypeError: testTryOne() missing 1 required positional argument: 'i'

Process finished with exit code 1

由此可以看出,捕获所有异常程序,则出现任何错误都不会是程序中断.但是同时捕获所有异常,有事会使程序出现异常时,程序员不知所措,找不到问题所在.

注意:一般情况下,应在程序中指明所要捕获的异常,而不是简单的捕获所有异常.

此外,异常处理的try语句也是可以嵌套的.

7.3 手工抛出异常

    所有的异常都是在程序运行中出现了错误而引发的异常,程序员还可以在Python程序中使用raise语句来引发指定的异常,并向异常传递数据.

    为了程序的需要,程序员还可以自定义新的异常类型,例如对用户输入的文本的长度的要求,则可以使用raise引发异常,以确保文本输入的长度符合要求.

7.3.1 用raise手工抛出异常

     使用raise引发异常十分简单,raise有以下几种使用方式:

    raise    异常名

    raise    异常名,附加数据

    raise    类名

使用raise可以抛出各种预定的异常,即使程序在运行时根本不会引发该异常.

[实例7-6]演示了程序中使用了代码抛出异常,因为没有捕获该异常,所以程序运行会中断,导致后面的代码不能运行.

def testRaise():
    for i in range(5):
        if i == 2:
            raise NameError
        print(i)
    print('end')

testRaise()

[代码说明]演示了代码中定义了函数testRaise,函数中是一个for循环,当循环变量i为2时,抛出NameError异常,因没有处理该异常而导致程序运行中断,后面的所有输出都得不到执行.

[运行效果]

0
Traceback (most recent call last):
1
  File "D:/PycharmProjects/untitled/YangQing/异常和日志.py", line 80, in <module>
    testRaise()
  File "D:/PycharmProjects/untitled/YangQing/异常和日志.py", line 76, in testRaise
    raise NameError
NameError

Process finished with exit code 

[实例7-7]演示了程序中使用了代码抛出异常,同时捕获该异常,因此程序运行不会中断

def testRaise2():
    for i in range(5):
        try:
            if i==2:
                raise NameError
        except NameError:
            print('Raise a NameError!')
        print(i)
    print('end')
testRaise2()

[代码说明]代码中定义了函数testRaise2,函数中是一个for循环,当循环变量i为2时,抛出NameError异常,但是这个异常引发会被捕获处理,程序就不会中断,后面的所有输出都得到执行.

[运行效果]

0
1
Raise a NameError!
2
3
4
end

Process finished with exit code 0

7.3.2 assert语句

    assert语句的一般形式如下:

       assert    <条件测试>,<异常附加数据>                 #其中异常附加数据是可选的

    assert语句是简化的raise语句,它引发异常的前提是其后的条件测试为假.

[实例7-8]演示了程序中使用assert抛出异常,同时捕获了该异常.代码如下:

def testAssert():
    for i in range(3):
        try:
            assert i<2
        except AssertionError:
            print('Raise a AssertionError!')
        print(i)
    print('end...')

testAssert()

[代码说明]代码中定义了函数testAssert,函数中是一个for循环,当循环变量是2时,assert后的条件测试为假,抛出AssertionError异常,但是这个异常引发会被捕获处理,程序不会中断,后面的所有输出都得到执行.

[运行效果]程序的运行效果如图:

0
1
Raise a AssertionError!
2
end...

Process finished with exit code 0

    assert语句一般用于在程序开发中测试代码的有效性.比如某个变量的值必须在一定范围内,而运行时得到的值不符合要求,则引发该异常,对于开发者予以提示.所以一般在程序开发中,不去捕获这个异常,而是让它中断程序.原因是程序中已经出现了问题,不应继续运行.

    assert语句并不是总运行的,只有Python中内置的一个特殊变量__debug__为True时才运行.要关闭程序中的assert语句就使用python  -O(短画线,后接大写字母O)来运行.如实例7-8用这个方法运行的结果如图7.10所示,很明显其中的assert语句被关闭,没有运行并引发异常:

D:\PycharmProjects\untitled\YangQing>python -O 异常和日志.py
0
1
2
end...

7.3.3    自定义异常类

    在python中定义异常类不用从基础完全定义,只要通过继承Exception类来创建自己的异常类.异常类的定义和其他类没有区别,最简单的自定义异常类甚至可以只继承Exception类,类体为pass 如:

class MyError(Exception):                  #继承Exception类
    pass

如果需要异常类带有一定的提示信息,也可以重载__init__和__str__这两个方法.

[实例7-9]演示了程序自定义了一个异常类,并用代码引发异常,代码如下:

class RangeError(Exception):
    def __init__(self,value):
        self.value = value

    def __str__(self):
        return self.value

raise RangeError('Range Error!')

[代码说明]代码中定义了一个继承了Exception类的异常类,并重载了__init__和__str__两个方法,之后,直接用raise抛出这个自定义的异常.

[运行效果]如图.

Traceback (most recent call last):
  File "D:/PycharmProjects/untitled/YangQing/异常和日志.py", line 122, in <module>
    raise RangeError('Range Error!')
__main__.RangeError: Range Error!

Process finished with exit code 1

7.4    用pdb调试程序

    python解释器可以发现程序中的语法错误,在试运行时会中断执行并给出提示.但是程序中逻辑上的错误,或其他非语法错误不会被发现.虽然程序能够正常运行,但是运行后得不到预想的结果,这就要对程序进行调试.

    调试程序可以用python自带的pdb模块,其功能有设置断点,单步执行,查看变量等.他可以用命令行参数的形式启动,也可以通过导入模块使用.常用的pdb模块的函数可以分为以下几类:

7.4.1调试语句块函数

 pdb模块中的调试语句块的函数及参数原型为:

run(statement[,globlas[,locals])       

  • statement    要调试的语句块,以字符串的形式表示;
  • globals      可选参数,设置statement运行的全局环境变量;
  • locals    可选参数,设置statement运行时的局部环境变量;

[实例7-10]交互模式下pdb模块调试,代码如下:

import pdb
pdb.run("""
for i in range(3):
    print(i)
""")

[代码说明]代码中首先导入了pdb模块,调用pdb模块的run函数来调试一段简单的Python代码(字符串形式).

[运行效果]如下图所示,是交互模式下调试,其中"(pdb)"是pdb调试的提示符.在提示符下使用h(elp)命令可以查看所有的调试命令.n命令表示执行语句的下一句;continue命令表示继续执行以后的程序段.此外,print(r)命令可用于输出变量的当前值,其它命令参考表7.2:

    <string>(3)<module>()则表示即将执行代码语句行数和所在模块为匿名模块.

> <string>(2)<module>()
(Pdb) h

Documented commands (type help <topic>):
========================================
EOF    c          d        h         list      q        rv       undisplay
a      cl         debug    help      ll        quit     s        unt      
alias  clear      disable  ignore    longlist  r        source   until    
args   commands   display  interact  n         restart  step     up       
b      condition  down     j         next      return   tbreak   w        
break  cont       enable   jump      p         retval   u        whatis   
bt     continue   exit     l         pp        run      unalias  where    

Miscellaneous help topics:
==========================
exec  pdb

(Pdb) exec
<built-in function exec>
(Pdb) n
> <string>(3)<module>()
(Pdb) continue
0
1
2

Process finished with exit code 0

完整命令            简写命令                            描述

args                   a                         打印当前的函数的参数

clear                  cl                         清除断点

break                 b                         设置断点

condition            无                        设置条件断点

continue            c或者count            继续运行,只要遇到断点或者程序结束

disable              无                         禁用断点

enable                无                        启用断点

help                    h                        查看pdb帮助

ignore                无                        忽略断点

jump                   j                          跳转到指定行数运行

list                    l                            列出程序清单

next                n                            执行下条语句,遇到函数不进入其内部

p                    p                                退出pdb

return            r                                一直运行到函数返回

tbreak            无                            设置临时断点,断点只中断一次

step                    s                            执行下一条语句,遇到函数进入其内部

where                w                            查看所在的位置

!                        无                            在pdb中执行语句

7.4.2 调试函数

pdb模块中的调试函数应当调用runcall函数,其参数原型为:

runcall(function[,argument,...])

  • function                                     函数名;
  • argument                                    函数的参数;
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值