Python基础(十一) 异常处理

在程序运行过程中,总会遇到各种各样的错误,有的错误是程序编写有问题造成的,比如本来应该输出整数结果输出了字符串,这样的错误我们通常称之为BUG,BUG是必须修复的。在Python中内置了一套异常处理机制,来帮助我们进行过错误处理,此外我们也需要跟踪程序的执行,查看变量的值是否正确,这个过程称为调试。Python的pdb可以让我们以单步方式执行代码。下面开始今天的内容

一、捕获错误

1、异常介绍

在编程过程中为了增强友好性,在程序出现bug时一般不会讲错误信息显示给用户,而是现实一个提示的页面,通俗的来说就是不让用户看见大黄页。


1

2

3

4

try:

    pass

except Exception as ex:

    pass

需求:将用户输入的两个数字相加。


1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

#!/usr/bin/env python

#-*- coding:utf-8 -*-

 

while True:

    num1 = input(‘input num1:‘)

    num2 = input(‘input num2:‘)

    try:

        num1 = int(num1)

        num2 = int(num2)

    except Exception as e:          #捕获任何异常

        print(‘出现异常,信息如下:‘)

        print(e)

#结果:

input num1:test

input num2:1

出现异常,信息如下:

invalid literal for int() with base 10‘test‘

2、异常种类

python中的异常种类非常多,每个异常专门用于处理某一个项异常。


1

2

3

4

5

6

7

8

9

10

11

12

13

AttributeError     #试图访问一个对象没有的树形,比如foo.x,但是foo没有属性x

IOError            #输入/输出异常;基本上是无法打开文件

ImportError        #无法引入模块或包;基本上是路径问题或名称错误

IndentationError   #语法错误(的子类) ;代码没有正确对齐

IndexError         #下标索引超出序列边界,比如当x只有三个元素,却试图访问x[5]

KeyError           #试图访问字典里不存在的键

KeyboardInterrupt  #Ctrl+C被按下

NameError          #使用一个还未被赋予对象的变量

SyntaxError Python #代码非法,代码不能编译(个人认为这是语法错误,写错了)

TypeError          #传入对象类型与要求的不符合

UnboundLocalError  #试图访问一个还未被设置的局部变量,基本上是由于另有一个同名的全局变量,导致你以为正在访问它

ValueError         #传入一个调用者不期望的值,即使值的类型是正确的

更多异常请参考官网地址:https://docs.python.org/3/library/exceptions.html#exception-hierarchy

下面举几个捕获异常的实例:

捕获IndexError错误:


1

2

3

4

5

6

7

8

9

10

11

#!/usr/bin/env python

#-*- coding:utf-8 -*-

 

dic = ["jack","eric"]

try:

    dic[10]

except IndexError as e:

    print(e)

#结果:

list index out of range

捕获KeyError错误:


1

2

3

4

5

6

7

8

9

10

11

#!/usr/bin/env python

#-*- coding:utf-8 -*-

dic = {‘k1‘:‘v1‘}

try:

    dic[‘k20‘]

except KeyError as e:    #捕获KeyError

    print(e)

#结果:

‘k20‘

捕获ValueError错误:


1

2

3

4

5

6

7

8

9

10

11

#!/usr/bin/env python

#-*- coding:utf-8 -*-

s1 = ‘hello‘

try:

    int(s1)

except ValueError as e:

    print(e)

#结果:

invalid literal for int() with base 10‘hello‘

对于上述实例,异常类只能用来处理指定的异常情况,如果非指定异常则无法处理。


1

2

3

4

5

6

7

8

9

10

11

12

#!/usr/bin/env python

#-*- coding:utf-8 -*-

s1 = ‘hello‘

try:

    int(s1)

except IndexError as e:   #如果未捕获到异常,程序直接报错

    print(e)

所以,写程序时需要考虑到Try代码块中可能出现的任意异常,这时可能想到我把能想到的错误都预先写好,但这不是最佳的解决办法,在Python中给我们提供了一个捕获万能异常的参数:Exception,它可以捕获任意异常,即:


1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

#!/usr/bin/env python

#-*- coding:utf-8 -*-

def foo(s):

    return 10 / int(s)

def bar(s):

    return foo(s) * 2

def main():

    try:

        bar("0")

    except Exception as e:    #捕获任意异常

        print(‘Error:‘,e)

if __name__==‘__main__‘:

    main()

#结果:

Error: division by zero

接下来你可能要问了,既然有这个万能异常,其他异常是不是就可以忽略了,NO!!对于特殊处理货提醒的异常需要先定义,最后定义Exception来确保程序正常运行。


1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

#!/usr/bin/env python

#-*- coding:utf-8 -*-

s1 = ‘hello‘

try:

    int(s1)

except KeyError as e:

    print(‘键错误‘)

except IndexError as e:

    print(‘索引错误‘)

except Exception as e:

    print(‘错误‘)

#结果:

错误

异常还有个更高级的结构:


1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

#!/usr/bin/env python

#-*- coding:utf-8 -*-

def foo(s):

    return 10 / int(s)

def bar(s):

    return foo(s) * 2

def main():

    try:                       #主代码块

        bar(‘0‘)

    except Exception as e:     #异常时,执行该步骤

        print(‘Error:‘, e)

    else:                      #主代码块执行完,不出错执行该步骤

        print(‘else....‘)

    finally:                   #无论异常与否,最终执行该步骤

        print(‘finally...‘)

if __name__==‘__main__‘:

    main()

#结果:

Error: division by zero

finally...

二、记录错误

如果不捕获错误,自然可以让Python解释器来打印出错误,但程序也被结束了。既然我们能捕获错误,就可以把错误打印出来,然后分析错误原因,同时,让程序继续执行下去。

我们之前介绍过日志模块logging,可以非常容易地记录错误信息:


1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

#!/usr/bin/env python

#-*- coding:utf-8 -*-

import logging

def foo(s):

    return 10 / int(s)

def bar(s):

    return foo(s) * 2

def main():

    try:

        bar(‘0‘)

    except Exception as e:

        logging.exception(e)

main()

print(‘END‘)

执行上面代码同样是出错看,但程序打印完错误信息后继续执行,并正常退出:


1

2

3

4

5

6

7

8

9

10

ERROR:root:division by zero

Traceback (most recent call last):

  File "E:/Python_project/SOCKET 代码/my_code.py", line 15in main

    bar(‘0‘)

  File "E:/Python_project/SOCKET 代码/my_code.py", line 11in bar

    return foo(s) * 2

  File "E:/Python_project/SOCKET 代码/my_code.py", line 8in foo

    return 10 / int(s)

ZeroDivisionError: division by zero

END

通过配置,logging还可以把错误记录到日志文件里,方便事后排查。

三、抛出错误

因为错误是Class,捕获一个错误就是捕获到该class的一个实例。因此,错误并不是凭空产生的,而是有意创建并抛出的。Python的内置函数会抛出很多类型的错误,我们自己编写的函数也可以抛出错误。

如果要抛出错误,首先根据需要,可以定义一个错误的Class,选择好继承关系,然后用raise语句抛出一个错误的实例:


1

2

3

4

5

6

7

8

9

10

11

12

13

#!/usr/bin/env python

#-*- coding:utf-8 -*-

class Foo(ValueError):

    pass

def foo(s):

    = int(s)

    if == 0:

        raise Foo(‘invalid value:%s‘%s)   #调用raise,抛出异常

    return 10 / n

foo(‘0‘)

执行上面的代码,我们可以跟踪到我们定义的错误:


1

2

3

4

5

6

Traceback (most recent call last):

  File "E:/Python_project/SOCKET 代码/my_code.py", line 13in <module>

    foo(‘0‘)

  File "E:/Python_project/SOCKET 代码/my_code.py", line 10in foo

    raise Foo(‘invalid value:%s‘%s)

__main__.Foo: invalid value:0

只有在必要的时候才定义我们自己的错误类型,如果可以选择Python已有的内置的错误类型(比如ValueError,TyprError),尽量使用Python内置的错误类型。


1

2

3

4

5

6

7

8

9

10

11

12

class MyException(Exception):

 

    def __init__(self, msg):

        self.message = msg

 

    def __str__(self):

        return self.message

 

try:

    raise MyException(‘我的异常‘)

except MyException as e:

    print(e)

四、单元测试

1、如何编写单元测试

首先来介绍一个概念(断言),凡是用print()来辅助查看的地方,都可以用断言(assert)来替代。

单元测试是用来对一个模块、一个函数或者一个类进行正确性检验的测试工作。

比如对内置函数abs(),我们可以编写出一下几个测试用例:

  • 输入整数,比如1、1.2、0.99,期待返回值与输入相同;
  • 输入负数,比如-1、-1.2、-0.99,期待返回值与输入相反;
  • 输入0,期待返回0;
  • 输入非数值类型,比如None、[]、{},期待抛出TypeError。

把上面的测试用例放到一个测试模块里,就是一个完整的单元测试。

如果单元测试通过,说明我们测试的这个函数能够正常工作。如果单元测试不通过,要么函数有bug,要么测试条件输入不正确,总之,需要修复使单元测试能够通过。

单元测试通过后有什么意义呢?

如果我们队abs()函数代码做了修改,只需要跑一遍单元测试,如果通过,说明我们的修改不会对abs()函数原有的行为造成影响,如果测试不通过,说明测试不通过,说明我们的修改与原有行为不一致,要么修改代码,要么修改测试。这种以测试为驱动的开发模式最大的好处就是确保一个程序模块的行为符合我们设计的测试用例。在将来修改的时候,可以极大程度地保证该模块行为仍是正确的。

下面我们来编写一个Dict类,这个类的行为和dict一致,但是可以通过属性来访问,用起来就像下面这样:


1

2

3

4

5

6

7

8

9

10

11

12

13

14

#自己定义的代码mydict.py

class Dict(dict):

    def __init__(self**kw):

        super().__init__(**kw)

    def __getattr__(self, key):

        try:

            return self[key]

        except KeyError:

            raise AttributeError(r"‘Dict‘ object has no attribute ‘%s‘" % key)

    def __setattr__(self, key, value):

        self[key] = value

为了编写单元测试,我们需要引入Python自带的unittest模块,编写如下:


1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

import unittest

from mydict import Dict

class TestDict(unittest.TestCase):

    def test_init(self):

        = Dict(a=1, b=‘test‘)

        self.assertEqual(d.a, 1)

        self.assertEqual(d.b, ‘test‘)

        self.assertTrue(isinstance(d, dict))

    def test_key(self):

        = Dict()

        d[‘key‘= ‘value‘

        self.assertEqual(d.key, ‘value‘)

    def test_attr(self):

        = Dict()

        d.key = ‘value‘

        self.assertTrue(‘key‘ in d)

        self.assertEqual(d[‘key‘], ‘value‘)

    def test_keyerror(self):

        = Dict()

        with self.assertRaises(KeyError):

            value = d[‘empty‘]

    def test_attrerror(self):

        = Dict()

        with self.assertRaises(AttributeError):

            value = d.empty

编写单元测试时,我们需要编写一个测试类,从unittest.TestCase继承。

以test开头的方法就是测试方法,不以test开头的方法不被认为是测试方法,测试的时候不会被执行。

对每一类测试都需要编写一个test_xx()方法。由于unittest.TestCase提供了很多内置的条件判断,我们只需要调用这些方法就可以断言输出是否是我们所期望的,最常用的断言就是assertEqual():


1

self.assertEqual(abs(-1),1)      #断言函数返回的结果与1相等

另一种中烟的断言就是期待抛出指定类型的Error,比如通过d[‘empty‘]访问不存在的key时,断言会抛出KeyError:


1

2

with self.assertRaises(KeyError):

    value = d[‘empty‘]

而通过d.empty访问不存在的key时,我们期待抛出AttributeError:


1

2

with self.assertRaises(AttributeError):

   value = d.empty

2、运行单元测试

一旦编写好单元测试,我们就可以运行单元测试。最简单的运行方式是在mydict_test.py的最后加上两行代码:


1

2

3

4

5

6

7

8

9

if __name__ == ‘__main__‘:

    unittest.main()

#执行结果:

.....

----------------------------------------------------------------------

Ran 5 tests in 0.000s

OK

3、setUp与tearDown方法

可以在单元测试中编写两个特殊的setUp()和tearDown()方法,这两个方法会分别在每调用一个测试方法的前后分别被执行。那这两个方法到底有什么用呢?

当我们测试需要连接启动一个数据库,这时就可以在setUp()方法中连接数据库,在tearDown()方法中关闭数据库,这样不必再每个测试方法中重复相同的代码。


1

2

3

4

5

6

7

class TestDict(unittest.TestCase):

    def setUp(self):

        print(‘setUp...‘)

    def tearDown(self):

        print(‘tearDown...‘)

可以再次运行测试看看每个测试方法调用前后是否会打印出setUp...和tearDown...。

总结一下,今天我们主要介绍了如何捕获错误信息,记录错误和单元测试,单元测试在我们日后的工作中很是重要,可以检测自己写的程序是否有BUG,单元测试通过了并不意味着程序就没有BUG了,单数不通过程序肯定有BUG。

时间: 2024-12-29 12:00:21

Python基础(十一) 异常处理的相关文章

Python 基础之 异常处理

python 基础之异常处理 说到异常处理,就得先问一下,什么是异常处理?  先来看一下,什么是异常? 异常就是:程序运行时发出的错误的信号. 异常的种类先来看一下: 一.常见的异常 AttributeError 试图访问一个对象没有的树形,比如foo.x,但是foo没有属性x IOError 输入/输出异常:基本上是无法打开文件 ImportError 无法引入模块或包:基本上是路径问题或名称错误 IndentationError 语法错误(的子类) :代码没有正确对齐 IndexError

python基础之异常处理

--引入 程序运行时,难免出现bug,当出现bug时,用户可能很难明白那一堆报错代码到底是个什么东西,为了让用户能更清楚知道错误原因或者直接对用户将此错误屏蔽,异常处理就应运而生. --格式 try: #输入要运行的代码 pass except NameError as err: #将该类型的错误捕获 pass except Exception as err: #将所有类型的错误捕获 pass else: #如果没有错误,则运行该段代码 pass finally: #不管是否存在错误,都运行该段

Python基础(十一) 类继承

类继承: 继承的想法在于,充份利用已有类的功能,在其基础上来扩展来定义新的类. Parent Class(父类) 与 Child Class(子类): 被继承的类称为父类,继承的类称为子类,一个父类,可以有多个子类: 子类,一旦继承父类,就拥有了父类的属性与方法,根据需要可以进行增删改. 这种做法的主要好处之一就是代码重用. 示例代码1: #*_*coding:utf-8*_* class perent_class(object): def __init__(self,name): self.n

python基础(21):异常处理

1. 异常和错误 1.1 错误 程序中难免出现错误,而错误分成两种 1.1.1 语法错误 语法错误:这种错误,根本过不了python解释器的语法检测,必须在程序执行前就改正. #语法错误示范一 if #语法错误示范二 def test: pass #语法错误示范三 print(haha 1.1.2 逻辑错误 #用户输入不完整(比如输入为空)或者输入非法(输入不是数字) num=input(">>: ") int(num) #无法完成计算 res1=1/0 res2=1+'s

Python基础、异常处理

一.概述 错误与异常的概念,区别 内置异常 为什么使用异常.场景.作用 异常处理 try/except/else try/fianlly raise assert 自定义异常类

python基础七--异常处理

异常处理try,except,else,finally的使用和实例. 1.异常处理说明 try: 5/0 except Exception as e: #这个exception 能捕捉到所有的异常 print('异常信息:',e) #这个是出了异常的话,怎么处理,e代表异常信息 else: print('没有出异常的话,走这里') #若是没有出现异常,运行else finally: print('这里是finally') #无论是否出现异常,都会执行finally 2.判断小数(判断s是否可以转

Python基础语法 - 异常处理

基本语法 在编程过程中为了增加友好性,在程序出现bug时一般不会将错误信息显示给用户,而是现实一个提示的页面,通俗来说就是不让用户看见大黄页! try: pass except Exception as e: pass 需求:将用户输入的两个数字相加 while True: num1 = raw_input('num1:') num2 = raw_input('num2:') try: num1 = int(num1) num2 = int(num2) result = num1 + num2

Python自动化 【第七篇】:Python基础-面向对象高级语法、异常处理、Scoket开发基础

本节内容: 1.     面向对象高级语法部分 1.1   静态方法.类方法.属性方法 1.2   类的特殊方法 1.3   反射 2.     异常处理 3.     Socket开发基础 1.     面向对象高级语法部分 1.1   静态方法.类方法.属性方法 1)   静态方法 通过@staticmethod装饰器即可把其装饰的方法变为一个静态方法.普通的方法,可以在实例化后直接调用,并且在方法里可以通过self.调用实例变量或类变量,但静态方法是不可以访问实例变量或类变量的,一个不能访

Python基础学习笔记(十一)函数与模块

参考资料: 1. <Python基础教程> 2. http://www.runoob.com/python/python-functions.html 3. http://www.liaoxuefeng.com/wiki/001374738125095c955c1e6d8bb493182103fac9270762a000 4. http://www.runoob.com/python/python-modules.html 5. http://www.pythoner.com/ 6. http

Python基础---python中的异常处理

Python中的异常处理 一.什么是异常处理 python解释器检测到错误,触发异常(也允许程序员自己触发异常) 程序员编写特定的代码,专门用来捕捉这个异常(这段代码与程序逻辑无关,与异常处理有关) 如果捕捉成功则进入另外一个处理分支,执行你为其定制的逻辑,使程序不会崩溃,这就是异常处理. 二.为什么要进行异常处理 python解析器去执行程序,检测到了一个错误时,触发异常,异常触发后且没被处理的情况下,程序就在当前异常处终止,后面的代码不会运行,谁会去用一个运行着突然就崩溃的软件. 所以你必须