微信公众号:码农充电站pro

个人主页:https://codeshellme.github.io

要么做第一个,要么做最好的一个。

目录

我们在编写程序时,总会不自觉的出现一些错误,比如逻辑错误语法错误和一些其它的运行时错误等。

  • 逻辑错误: 这种错误不会导致程序崩溃,它不容易被发现,只有在执行结果不是我们预期的时候,才会被发现。
  • 语法错误: 这种错误是不符合语法规定的错误,说白了,就是编译器或者解释器无法理解的代码。出现这种错误时,程序是不能运行的。
  • 其它运行时错误: 这种错误是程序在运行的过程中出现的,一般情况下不会出现,但是极端情况下会出现,是程序编写者考虑不够周全导致的。

在写程序时一定要把所有的情况都考虑到,并且处理掉,不能有侥幸心理(认为某种情况不会出现)。在程序中,只要是有可能出现的情况,那就一定会出现。

程序员也喜欢将这些错误戏称为bugbug代表软件系统中的漏洞缺陷bug需要修正,否则程序是无法正常运行的。重要的软件系统如果出现漏洞,会带来巨大的危害。因此,在软件初步完成后,要进行严格的,全面的测试,否则将漏洞百出。

Python 中提供了一套处理错误的机制,叫做异常

比较普通的处理错误的方法,是使用if 语句断言assert来对各种情况进行判断,从而进行相应的处理。而异常是一种更加高级的处理错误的机制。

1,常见异常

我们来看一些Python 中常见的异常。

SyntaxError 异常

Python 语言有自己的语法格式和规则,如果我们没有遵守这些规则,将会出现异常:

  1. >>> print('abc')- # 右括号后边有一个横线
  2. File "<stdin>", line 1
  3. print('abc')-
  4. ^
  5. SyntaxError: invalid syntax

上尖括号^指出了异常出现的的位置。

NameError 异常

如果我们使用了一个未定义的变量,将会出现该异常:

  1. >>> print(a) # a 变量未定义
  2. Traceback (most recent call last):
  3. File "<stdin>", line 1, in <module>
  4. NameError: name 'a' is not defined

ZeroDivisionError 异常

进行除法运算时,如果除数为0,将会该异常:

  1. >>> 1 / 0 # 除数为 0
  2. Traceback (most recent call last):
  3. File "<stdin>", line 1, in <module>
  4. ZeroDivisionError: division by zero

如果程序没有处理异常,在异常出现时,将会崩溃退出,Python 解释器会为你定位到异常出现的位置,有助于快速的解决异常。

2,处理异常

异常需要捕获,从而处理异常。如果在发生异常时,这个异常没有被捕获处理,这个异常将会一层一层的向上抛,直到这个异常被捕获处理,或者程序崩溃。

try except 语句

在Python 中使用try 语句块来捕获异常,在except 语句块中处理异常。

我们一般将有可能出现异常的语句放在try 语句块中,在except 语句块中编写处理异常的措施。

比如,我们有如下代码:

  1. def hello(s):
  2. print('hello %s' % s)
  3. hello('123', 'abc')

其中hello 函数的定义是需要接收一个参数,而在调用时,传递了两个参数,运行此代码将出现如下异常:

  1. Traceback (most recent call last):
  2. File "Test.py", line 8, in <module>
  3. hello('123', 'abc')
  4. TypeError: hello() takes 1 positional argument but 2 were given

代码在遇到异常时,会在异常代码处抛出异常,后边的代码将不会再执行。如果异常代码没有处理,程序将崩溃退出。

我们可以这样处理该异常:

  1. try:
  2. hello('123', 'abc')
  3. except Exception as e:
  4. print(e)

我们将调用语句放在了try 语句块中,这样就可以捕获异常。except 关键字的后边是要捕获的异常的名字as 后边是捕获到的异常 e。在except 语句块中,我们只是将捕获到的异常打印了出来,运行该代码,结果如下:

  1. hello() takes 1 positional argument but 2 were given

可见错误被打印了出来。我们还可以在打印错误之后,再正确的调用hello 函数:

  1. try:
  2. hello('123', 'abc')
  3. except Exception as e:
  4. print(e)
  5. hello('123')

运行结果如下:

  1. hello() takes 1 positional argument but 2 were given
  2. hello 123

可见,运行到hello('123', 'abc') 时,出现异常,然后代码执行到except 语句块,print(e) 将错误打印出来,又执行了hello('123')

打印异常是最简单的处理异常的方式,在工作中,我们会将异常信息记录在日志文件中,这样可以将异常记录下来,以便处理异常。

一般在捕获异常时,尽量只在try 语句块中编写有可能发生异常的代码,基本不会发生异常的语句不要写在try 块中,这样可以减少try 块中的代码量,有助于定位问题。

except 语句

except 关键字后边可以跟一个异常名字,也可以跟一组异常名字,一组异常时,将多个异常的名字写在一个元组中,语法如下:

  1. except (Error1, Error2...):
  2. pass

一个try 语句中,也可以包含多个except 语句块,语法如下:

  1. try:
  2. # 代码块
  3. except Error1 as e:
  4. # 处理 Error1
  5. except Error2 as e:
  6. # 处理 Error2
  7. .
  8. .
  9. .
  10. except:
  11. e = sys.exc_info()[0])
  12. pass

多个except 语句块时,Python 解释器会从上到下依次判断异常的类型,直到符合某个异常时,会执行对应的语句块中的代码,在该except 块之后的except 块将被忽略。

其中,最后一个except 块可以省略异常的名字,这种格式可以匹配任意的异常,在该块中,可以使用sys.exc_info()[0] 来获取异常。

在有多个except 语句块时,要注意,前边的异常的范围应该小于等于后边的异常的范围,否则,后边的except 块将没有意义。

else 语句

Python 中,try... except... 之后还可以有一个else语句块,except 语句块是在遇到异常时执行的,else 语句块是在没有遇到异常时执行的。

发生异常时,示例:

  1. try:
  2. hello('123', 'abc')
  3. except Exception as e:
  4. print('发生异常')
  5. print(e)
  6. else:
  7. print('没有发生异常')

上面的代码中,执行到hello('123', 'abc') 时会发生异常,然后会进入到except 语句块,else 语句不会被执行,执行结果如下:

  1. 发生异常
  2. hello() takes 1 positional argument but 2 were given

没有发生异常时,示例:

  1. try:
  2. hello('123')
  3. except Exception as e:
  4. print('发生异常')
  5. print(e)
  6. else:
  7. print('没有发生异常')

上面代码中的try 语句块不会发生异常,那么except 语句就不会执行,else 语句会执行,结果如下:

  1. hello 123
  2. 没有发生异常

注意:

else 语句的使用频率并不高。

finally 语句

finally 语句块无论是否异常都会被执行,该语句块经常用在需要关闭系统资源的情况下。

没有发生异常时,示例如下:

  1. try:
  2. hello('123')
  3. except Exception as e:
  4. print('发生异常')
  5. print(e)
  6. else:
  7. print('没有发生异常')
  8. finally:
  9. print('执行了 finally 语句块')

上面代码中,try 语句块没有发生异常,elsefinally 都被执行,except 语句块没有执行。运行结果如下:

  1. hello 123
  2. 没有发生异常
  3. 执行了 finally 语句块

发生异常时,示例如下:

  1. try:
  2. hello('123', 'abc')
  3. except Exception as e:
  4. print('发生异常')
  5. print(e)
  6. else:
  7. print('没有发生异常')
  8. finally:
  9. print('执行了 finally 语句块')

上面代码中,try 语句块发生异常,exceptfinally 都被执行,else 语句块没有执行。运行结果如下:

  1. 发生异常
  2. hello() takes 1 positional argument but 2 were given
  3. 执行了 finally 语句块

注意:

else 语句块与finally 语句块可以同时存在,也可以同时不存在,也可以一个存在一个不存在,互不影响。

3,抛出异常

raise 异常

如果你捕获了一个异常,却不想彻底解决这个异常,而想将该异常向上层抛出,可以使用raise 关键字。

raise 用于抛出异常,其后可以跟一个异常对象,或者什么也不跟。

raise 后跟一个异常对象

  1. raise Exception('这里发生了错误')

raise 后什么也不跟:

  1. try:
  2. hello('123', 'abc')
  3. except Exception as e:
  4. print('发生异常')
  5. raise

Python 解释器会记录最后一个发生的异常,raise 会将最后一个异常抛出。上面代码中的raise 相当于raise e

assert 断言

assert 语句称为断言,就是判断某个表达式是否为真:

  • 表达式为True 时,正常通过
  • 表达式为False 时,抛出AssertionError 异常

示例如下:

表达式1 == 1True,没有反应:

  1. >>> assert 1 == 1

表达式1 == 0False,抛出异常:

  1. >>> assert 1 == 0
  2. Traceback (most recent call last):
  3. File "<stdin>", line 1, in <module>
  4. AssertionError

4,Python 异常层次结构

Python 异常层次结构如下:

  1. BaseException
  2. +-- SystemExit
  3. +-- KeyboardInterrupt
  4. +-- GeneratorExit
  5. +-- Exception
  6. +-- StopIteration
  7. +-- StopAsyncIteration
  8. +-- ArithmeticError
  9. | +-- FloatingPointError
  10. | +-- OverflowError
  11. | +-- ZeroDivisionError
  12. +-- AssertionError
  13. +-- AttributeError
  14. +-- BufferError
  15. +-- EOFError
  16. +-- ImportError
  17. | +-- ModuleNotFoundError
  18. +-- LookupError
  19. | +-- IndexError
  20. | +-- KeyError
  21. +-- MemoryError
  22. +-- NameError
  23. | +-- UnboundLocalError
  24. +-- OSError
  25. | +-- BlockingIOError
  26. | +-- ChildProcessError
  27. | +-- ConnectionError
  28. | | +-- BrokenPipeError
  29. | | +-- ConnectionAbortedError
  30. | | +-- ConnectionRefusedError
  31. | | +-- ConnectionResetError
  32. | +-- FileExistsError
  33. | +-- FileNotFoundError
  34. | +-- InterruptedError
  35. | +-- IsADirectoryError
  36. | +-- NotADirectoryError
  37. | +-- PermissionError
  38. | +-- ProcessLookupError
  39. | +-- TimeoutError
  40. +-- ReferenceError
  41. +-- RuntimeError
  42. | +-- NotImplementedError
  43. | +-- RecursionError
  44. +-- SyntaxError
  45. | +-- IndentationError
  46. | +-- TabError
  47. +-- SystemError
  48. +-- TypeError
  49. +-- ValueError
  50. | +-- UnicodeError
  51. | +-- UnicodeDecodeError
  52. | +-- UnicodeEncodeError
  53. | +-- UnicodeTranslateError
  54. +-- Warning
  55. +-- DeprecationWarning
  56. +-- PendingDeprecationWarning
  57. +-- RuntimeWarning
  58. +-- SyntaxWarning
  59. +-- UserWarning
  60. +-- FutureWarning
  61. +-- ImportWarning
  62. +-- UnicodeWarning
  63. +-- BytesWarning
  64. +-- ResourceWarning

可参考这里:

https://docs.python.org/3/library/exceptions.html#exception-hierarchy

这些都是内建异常BaseException 是所有异常的父类,我们使用最多的是Exception 及其子类。可以使用help(类名)来查看每个类的详情。

5,自定义异常

有时候,我们需要定义自己的异常类,来满足自己的需求。

我们已经知道,Python 异常类有自己的层次结构,所有的类都直接或者间接继承了BaseException。因此,用户自定义的异常类,也需要满足这种层次结构。

一般情况下,自定义异常需要继承Exception 类。如下:

  1. class MyError(Exception):
  2. pass

MyError 类的使用方式,跟内建异常类的使用方式一样。你可以根据自己的需要,为MyError 类编写相应的构造方法,和其它类方法。

如果没有为MyError 编写构造方法,那么MyError 就继承了Exception 的构造方法。

6,调试错误

程序编写完成后不一定是正确的,当发现有错误时,就需要定位错误的位置。

最普遍,最简单的调错的方法就是打印某个变量,通过输出变量的值,来查看其是否是你想要的结果。

另一种比较高效,有力的调试代码的方式是单步调试,即是通过设置断点,深入到代码内部,一步一步的跟踪查看代码的执行结果是否正确,从而达到修正代码的目的。

C 语言中有一个非常著名的工具叫做gdb,这是一款强大的调试工具。Python 中也有类似的一款工具叫做pdb,它使用起来要比gdb 简单许多。

在Python 中,pdb 是一个模块,所以,在使用之前要先使用import pdb 将该模块引入。然后,在需要调试代码的地方,使用pdb.set_trace() 方法来设置断点,在代码执行到此处时,Python 解释器就会从此处开始让你调式代码。

如下代码,文件名为Test.py

  1. #! /usr/bin/env python3
  2. # 引入 pdb 模块
  3. import pdb
  4. def hello(s):
  5. print('hello %s' % s)
  6. # 设置断点
  7. pdb.set_trace()
  8. hello('python')
  9. hello('java')

我们使用python3 来运行该程序,如下:

  1. $ python3 Test.py
  2. > ~/Test.py(12)<module>()
  3. -> hello('python')
  4. (Pdb)

可以看到代码在hello('python')之前暂停并进入断点,控制台显示出(Pdb),我们可以在这个后面输入Python 代码或者pdb 支持的命令。

pdb 常用命令如下:

  • n:进行下一步代码,即单步执行
  • c:代码执行到下一个断点处,如果没有下一个断点,则执行到程序结束
  • s:在遇到函数时,使用s 命令,可以进入函数内部
  • l:列出当前语句周围的10行代码
  • p:用于输出变量的值,相当于print 函数

(完。)

推荐阅读:

Python 简明教程 --- 18,Python 面向对象

Python 简明教程 --- 19,Python 类与对象

Python 简明教程 --- 20,Python 类中的属性与方法

Python 简明教程 --- 21,Python 继承与多态

Python 简明教程 --- 22,Python 闭包与装饰器


欢迎关注作者公众号,获取更多技术干货。

Python 简明教程 --- 23,Python 异常处理的更多相关文章

  1. Python 简明教程 --- 24,Python 文件读写

    微信公众号:码农充电站pro 个人主页:https://codeshellme.github.io 过去的代码都是未经测试的代码. 目录 无论是哪种编程语言,IO 操作都是非常重要的部分.I 即Inp ...

  2. Python 简明教程 --- 25,Python 目录操作

    微信公众号:码农充电站pro 个人主页:https://codeshellme.github.io 做技术一定要一颗恒心,这样才不会半途而废. 目录 上一节我们介绍了文件相关的操作,本节我们来介绍目录 ...

  3. Python 简明教程 --- 26,Python 多进程编程

    微信公众号:码农充电站pro 个人主页:https://codeshellme.github.io 学编程最有效的方法是动手敲代码. 目录 1,什么是多进程 我们所写的Python 代码就是一个程序, ...

  4. 【笔记】Python简明教程

    Python简明教程,此资源位于http://woodpecker.org.cn/abyteofpython_cn/chinese/ s=u'中文字符' #u表示unicode,使用u之后能正常显示中 ...

  5. python简明教程

    Python简明教程 MachinePlay关注 0.7072018.09.26 01:49:43字数 2,805阅读 9,287 Python一小时快速入门 1.Python简介   pylogo. ...

  6. Python 简明教程 --- 8,Python 字符串函数

    微信公众号:码农充电站pro 个人主页:https://codeshellme.github.io 好代码本身就是最好的文档.当你需要添加一个注释时,你应该考虑如何修改代码才能不需要注释. -- St ...

  7. Python 简明教程 --- 5,Python 表达式与运算符

    微信公众号:码农充电站pro 个人主页:https://codeshellme.github.io 靠代码行数来衡量开发进度,就像是凭重量来衡量飞机制造的进度. -- Bill Gates 目录 1, ...

  8. 《Python简明教程》总结

    Python经典教程<Python简明教程> 目录: 为什么Python 安装Python 体验Python Python数据类型 运算符与表达式 控制流 函数 模块 数据结构 解决问题 ...

  9. python中global的用法——再读python简明教程

    今天看了知乎@萧井陌的编程入门指南,想重温一下 <python简明教程>,对global的用法一直不太熟练,在此熟练一下,并实践一下python中list.tuple.set作为参数的区别 ...

随机推荐

  1. kill杀死进程

    [root@queen ~]# kill 选项 %进程号 选项与参数: -l :这个是L 的小写,列出kill 能够使用的signal - :重新读取一次参数的设定档(类似reload) - :代表与 ...

  2. java scoket aIO 通信

    AsynchronousServerSocketChannel assc.accept(this, new ServerCompletionHandler()); 第一个参数是服务器的处理类,第二个参 ...

  3. RabbitMQ:三、进阶

    保证消息的安全 持久化 交换器持久化:声明交换器时指定持久化 队列持久化:声明队列时指定持久化 消息持久化:发送消息时指定持久化 一般队列和消息持久化要同时声明,此外消息假如进了交换器却找不到队列,也 ...

  4. 46道Linux面试题送给你(后续会不断更新)

    绝对路径用什么符号表示? 当前目录.上层目录用什么表示?主目录用什么表示? 切换目录用什么命令? 答案: # 绝对路径: 如/etc/init.d # 当前目录和上层目录: ./ ../ # 主目录: ...

  5. Chrome插件Postman的数据目录存储位置,记一次重装系统后找回postman数据的过程...

    有次重装系统到一块新的SSD磁盘,很多数据都做了备份就是忘记将Chrome插件Postman的数据做备份,导致重装后找不到以前定义的那些Collections.悔恨之余想到既然我原来的C盘还在,为何不 ...

  6. win7旗舰版安装 oracle 10g 不能进入图形界面的问题

    前阵子重装了系统,把dell机器自带的win7 64位(家庭版已升级旗舰版,装ORACLE正常)换回了32位系统,前两天因为一些软件开发的问题,需要把以前做的一个系统重新架起来,数据库用的是oracl ...

  7. [源码解析]Oozie来龙去脉之提交任务

    [源码解析]Oozie来龙去脉之提交任务 0x00 摘要 Oozie是由Cloudera公司贡献给Apache的基于工作流引擎的开源框架,是Hadoop平台的开源的工作流调度引擎,用来管理Hadoop ...

  8. STL初步学习(queue,deque)

    4.queue queue就是队列,平时用得非常多.栈的操作是只能是先进先出,与栈不同,是先进后出,与之后的deque也有区别.个人感觉手写队列有点麻烦,有什么head和tail什么的,所以说 STL ...

  9. 洛谷 P2296 【寻找道路】

    这道题真的很女少啊 言归正传: 这道题其实就是考验的思路,读题后,我们发现对于某个点他所连接的点必须连接终点,那么我们直接反向存图,从终点进行bfs,可以找到未连接的点,然后对这些点所连接的点进行标记 ...

  10. h5移动端实现图片文件上传

    PC端上传文件多半用插件,引入flash都没关系,但是移动端要是还用各种冗余的插件估计得被喷死,项目里面需要做图片上传的功能,既然H5已经有相关的接口且兼容性良好,当然优先考虑用H5来实现. JS代码 ...