Python学习笔记【八】——《python基础教程》:: 异常_贺二公子的博客-程序员宅基地

技术标签: python  异常  [软件开发]python  

8. 异常

8.1. 什么是异常

  Python用异常对象(exception object)来表示异常情况。遇到错误后,会引发异常。若异常对象未被处理或捕捉,程序会用回溯(Traceback,一种错误信息)终止执行,例如:

>>> 1/0
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ZeroDivisionError: integer division or modulo by zero

  事实上,每个异常都是一些类的实例,可以被引发也可以被捕捉。当捕捉到这些异常并对其进行处理,可使程序继续运行。

8.2. 按自己的方式出错

  学习处理异常之前,先学习如何引发异常,以及自定义异常类型。  

8.2.1. raise语句

  可以使用一个类(Exception的子类)或实例参数调用raise语句来引发异常。当使用类时,程序会自动创建实例。例如:

#引发没有错误信息的普通异常
>>> raise Exception
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
Exception
#添加错误信息的异常
>>> raise Exception("hyperdrive overload")
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
Exception: hyperdrive overload

  内建的异常类有很多种(参考Python库参考手册的“Build-in Exception”一节),这些异常都可以在exceptions模块(和内建的命名空间)中找到,所有这些异常都可以用在raise语句中。

>>> import exceptions
>>> dir(exceptions)
['ArithmeticError', 'AssertionError', 'AttributeError', 'BaseException', 'BufferError', 'BytesWarning', 'DeprecationWarning', 'EOFError', 'EnvironmentError', 'Exception', 'FloatingPointError', 'FutureWarning', 'GeneratorExit', 'IOError', 'ImportError', 'ImportWarning', 'IndentationError', 'IndexError', 'KeyError', 'KeyboardInterrupt', 'LookupError', 'MemoryError', 'NameError', 'NotImplementedError', 'OSError', 'OverflowError', 'PendingDeprecationWarning', 'ReferenceError', 'RuntimeError', 'RuntimeWarning', 'StandardError', 'StopIteration', 'SyntaxError', 'SyntaxWarning', 'SystemError', 'SystemExit', 'TabError', 'TypeError', 'UnboundLocalError', 'UnicodeDecodeError', 'UnicodeEncodeError', 'UnicodeError', 'UnicodeTranslateError', 'UnicodeWarning', 'UserWarning', 'ValueError', 'Warning', 'ZeroDivisionError', '__doc__', '__name__', '__package__']

  下表描述了一些重要的内建异常类:

类名 描述
Exception 所有异常的基类
AttributeError 特性引用或赋值失败时引发
IOError 试图打开不存在文件(包括其他情况)时引发
IndexError 在使用序列中不存在的索引时引发
KeyError 在使用映射中不存在的键时引发
NameError 再找不到名字(变量)时引发
SyntaxError 在代码为错误形式时引发
TypeError 在内建操作或者函数应用于错误类型的对象时引发
ValueError 在内建操作或者函数应用于正确类型的对象,但是该对象使用不合适的值时引发
ZeroDivisionError 在除法或者模除操作的第二个参数为0时引发

8.2.2. 自定义异常类

  自定义异常类可以根据异常所在的类,选择性地处理当前类型的异常。
  创建异常类与创建其他类方法一样,只需确保从Exception类继承即可。

8.3. 捕捉异常

  可以使用try/except实现捕捉异常并作错误处理,例如:

>>> try:
...   x=input('enter the first number: ')
...   y=input('enter the second number: ')
...   print x/y
... except ZeroDivisionError:
...   print "The second number can't be zero!"
... 
enter the first number: 10
enter the second number: 0
The second number can't be zero!

  上述异常也可以通过if语句检查y值实现,但是try/except有以下优势:
  1. 若出现多个除法运算,需要对所有运算进行if判断,而是用try/except只需要一个错误处理器;
  2. try/except不会搞乱原来的代码,而if语句检查可能错误会降低代码可读性。
  ★若异常没有被捕捉,将会被传递到调用的函数中,直到程序最顶层
  
  若捕捉到的函数想重新引发,可以使用不带参数的raise。例如,定义一个可以屏蔽ZeroDivisionError异常的类:  

>>> class MuffledCalculator:
...   muffled=False
...   def calc(self, expr):
...     try:
...       return eval(expr)
...     except ZeroDivisionError:
...       if self.muffled:
...         print "Division by zero is illegal"
...       else:
...         raise

  使用示例如下:

>>> calculator=MuffledCalculator()
>>> calculator.calc("10/2")
5
>>> calculator.calc("10/0") #屏蔽未打开,捕捉并重新引发ZeroDivisionError异常
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 5, in calc
  File "<string>", line 1, in <module>
ZeroDivisionError: integer division or modulo by zero
>>> calculator.muffled=True
>>> calculator.calc("10/0") #屏蔽被打开,打印错误信息
Division by zero is illegal

8.4. 不止一个except子句

  8.3节中的第一个例子,若在提示符后面输入非数字类型的值,会产生另一个异常:

enter the first number: w
Traceback (most recent call last):
  File "<stdin>", line 2, in <module>
  File "<string>", line 1, in <module>
NameError: name 'w' is not defined

  可以在try/except后添加另一个except子句,用于捕捉另一个异常:
  

>>> try:
...   x=input('enter the first number: ')
...   y=input('enter the second number: ')
...   print x/y
... except ZeroDivisionError:
...   print "The second number can't be zero!"
... except TypeError:
...   print "That wasn't a number, was it?"
... 
enter the first number: 23
enter the second number: "That wasn't a number, was it?"
That wasn't a number, was it?

8.5. 用一个块捕捉两个异常

  若要用一个块捕捉多个异常类型,可以将它们作为元组列出。例如:  

>>> try:
...   x=input('enter the first number: ')
...   y=input('enter the second number: ')
...   print x/y
... except (ZeroDivisionError, TypeError, NameError):
...   print "Your numbers were bogus..."
... 

8.6. 捕捉对象

  若希望在except子句中访问异常对象本身,可以使用两个参数。例如,如果希望程序继续运行,但是又想记录错误时,可能用到该功能。

>>> try:
...   x=input('enter the first number: ')
...   y=input('enter the second number: ')
...   print x/y
... except (ZeroDivisionError, TypeError, NameError), e:
...   print e
... 
enter the first number: 34
enter the second number: aa
name 'aa' is not defined

8.7. 真正的全捕捉

  可以使用try/except指定要捕捉的异常类型,但是,程序员无法预测所有可能发生的异常,总会有所遗漏。可以在except子句中忽略所有的异常类,以实现捕捉所有异常:

>>> try:
...   x=input('enter the first number: ')
...   y=input('enter the second number: ')
...   print x/y
... except:
...   print "Something wrong happened..."
... 
enter the first number: 34
enter the second number: a
Something wrong happened...

!!!捕捉所有异常类是危险的,他会隐藏所有程序员未想到并且为做好准备处理的错误。甚至会捕捉用户中止执行的Ctrl+C操作,以及用sys.exit函数终止程序的操作。此时可以使用except Exception, e,或者对异常对象e进行一些检查。
  

8.8. 万事大吉

  之前的例子中,发生异常后往往只打印一个错误信息,对于产生的异常没有实际修正作用。上述例子可以像对条件和循环语句那样,给try/except添加else子句实现循环,实现只有在没有异常情况下退出,否则重新执行异常语句的功能。例如:

>>> while True:
...   try:
...     x=input("enter the first number: ")
...     y=input("enter the second number: ")
...     print x/y
...   except Exception, e: #捕获所有异常并保存到e
...     print "invalid input: ",e #打印捕获的异常
...     print "Please input again" #若有异常,则重新执行
...   else: #若没有异常,只想下面一行代码,退出循环
...     break
... 
enter the first number: 1
enter the second number: 0
invalid input:  integer division or modulo by zero
Please input again
enter the first number: dds
invalid input:  name 'dds' is not defined
Please input again
enter the first number: "asd"
enter the second number: "nmm"
invalid input:  unsupported operand type(s) for /: 'str' and 'str'
Please input again
enter the first number: 10
enter the second number: 2
5

8.9. 最后…

  finally子句,用于try/except之后的清理工作,无论是否发生异常,都会执行finally子句。例如:
  

>>> try:
...   1/1   #无异常发生时
... except:
...   print "except"
... finally:
...   print "finally"
... 
1
finally
>>> try:
...   1/0   #有异常发生时
... except:
...   print "except"
... finally:
...   print "finally"
... 
except
finally

  finally子句在代码执行后关闭文件或者套接字是非常有用。
  
  在Python2.5之前,finally子句需要独立使用,不能作为try/except子句使用。在Python2.5及其之后版本中,可以与try/except/else/finally组合使用。

8.10. 异常和函数

  若异常在函数内引发而不被处理,它就会被传递到函数调用的地方。若调用处没有被处理,它会继续传播,一直到达主程序(全局作用域)。若依然没有被处理,程序会带着堆栈跟踪中止。例如:
  

>>> def faulty():
...   raise Exception("Something is wrong")
... 
>>> def ignore_faulty():
...   faulty()
... 
>>> def handle_exception():
...   try:
...     faulty()
...   except:
...     print "Exception handled"
... 
>>> ignore_faulty() #faulty的异常传递到ignore_faulty,未处理异常,导致堆栈跟踪
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 2, in ignore_faulty
  File "<stdin>", line 2, in faulty
Exception: Something is wrong
>>> handle_exception() #faulty的异常传递到handle_exception,被处理
Exception handled

8.11. 异常之禅

  异常处理并不是很复杂。若知道某段代码可能导致某种异常,又不希望程序已堆栈跟踪的形式终止,可以根据需要添加try/except或try/finally语句进行处理。
  有时条件语句可以实现和异常处理同样的功能,但条件语句可能在自然性和可读性上差些。另一方面看,某程序使用if/else实现会比使用try/except要好。

  举个例子对比下if/else和try/except。假设有一个字典,希望打印出存储在特定的键下面的值。若不存在,则什么也不做。
  可以使用if/else实现,编码如下:

>>> def describePerson(person):
...   print "Description of", person["name"]
...   print "Age:", person["age"]
...   if "occupation" in person:
...     print "Occupation:", person["occupation"]
#提供名字和年龄字典时的输出
>>> holly={
   "name":"holly","age":55}
>>> describePerson(holly)
Description of holly
Age: 55
#提供名字、年龄和职业时的输出
>>> holly={
   "name":"holly","age":55, "occupation":"software engineer"}
>>> describePerson(holly)
Description of holly
Age: 55
Occupation: software engineer

  上述代码直观,但效率低。程序会两次查找“occupation”键——判断是否存在;获取键值。
  可以采用try/except语句实现,编码如下:

>>> def describePerson(person):
...   print "Description of", person["name"]
...   print "Age:", person["age"]
...   try:
...     print "Occupation:" + person["occupation"]#此处使用加号连接,若使用逗号,发生异常时"occupation:"会被输出
...   except KeyError: pass
... 
#提供名字和年龄字典时的输出
>>> holly={
   "name":"holly","age":55}
>>> describePerson(holly)
Description of holly
Age: 55
#提供名字、年龄和职业时的输出
>>> holly={
   "name":"holly","age":55, "occupation":"software engineer"}
>>> describePerson(holly)
Description of holly
Age: 55
Occupation:software engineer

  程序假定”occupation”键存在,若真实存在,则直接输出打印;否则会引发KeyError异常,被except捕捉处理。
  
  另一个例子,展示下try/except在查看对象是否存在某特性中的作用。假设要查看某对象是否有write特性,编码如下:

>>> try:
...   obj.write
... except AttributeError:
...   print "The object is not writeable"
... else:
...   print "The objece is writebale"

  try子句访问特性,若引发AttributeError异常,说明对象没这个特性,反之有。这跟getattr方法功能相同,事实上getattr内部实现也是使用了该方法。

上述代码获得的效率提高并不多,除非程序对性能有明确需求,否则开发人员不需要过多考虑优化的问题。
很多情况下,使用try/except比使用if/else更加”Python化”,应养成尽可能使用try/except语句。
try/except语句在Python中的表现可以用海军少将Grace Hooper的妙语解释:”请求宽恕易于请求许可”。在做一件事时去处理可能出现的错误,而不是在开始做事前就进行大量的检查,这个可略可以总结为习语”看钱就调(Leap Before You Look)”。

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/heli200482128/article/details/75481579

智能推荐

Istio Client-go -- ServiceEntry-程序员宅基地

查看ServiceEntrypackage mainimport ( "context" "log" "github.com/owenliang/k8s-client-go/common" "istio.io/api/networking/v1beta1" networkingv1beta1 "istio.io/client-go/pkg/apis/networking/v1beta1" versionedclient "istio.io/client-go/pkg/clientset/

Failed with result ‘start-limit-hit‘ 解决方案_july_young的博客-程序员宅基地

编辑docker的配置文件vim /etc/docker/daemon.json{ "registry-mirrors": ["https://kuamavit.mirror.aliyuncs.com", "https://registry.docker-cn.com", "https://docker.mirrors.ustc.edu.cn"], "max-concurrent-downloads": 10, "storage-driver": "overlay2", "grap_start-limit-hit

从CSV文件中读取数据_open("info.csv", 'r',encoding='gbk')_Pluto__315的博客-程序员宅基地

从CSV文件中读取数据,去掉内容中的逗号,打印到屏幕。生成ls的方法不同,效果有所差别import jsonf = open("学生信息表.csv","r",encoding = 'gbk')ls = []for line in f : line = line.replace("\n","") ls = line.split(",") print(ls)f.close()运行结果为:[‘学号’, ‘姓名’, ‘性别’, ‘班级’][‘17010001’, ‘张三_open("info.csv", 'r',encoding='gbk')

报错 com.alibaba.druid.pool.DruidPooledConnection cannot be cast to sun.rmi.transport.Connection_com.alibaba.druid.pool.druidpooledpreparedstatemen-程序员宅基地

大概率导错包了。在使用DruidDataSource连接MySQL时,连接失败,报错com.alibaba.druid.pool.DruidPooledConnection cannot be cast to sun.rmi.transport.Connection。我的原因是导入了错误的包。正确的应该是import java.sql.Connection;..._com.alibaba.druid.pool.druidpooledpreparedstatement.executequery(druidpooled

《写给大忙人看的java SE8》笔记 -- 1. lambda表达式_写给大忙人的se8-程序员宅基地

函数式接口只含有一个抽象方法的接口,称为函数式接口。lambda表达式可以赋值并且仅可以赋值给函数式接口的变量。实际上编译器往往需要根据函数式接口推断lambda表达式的参数类型和返回值,比如:Comparator comp = (first, second) -> Integer.compare(first.length(), second.length())。实际上,你甚至无法将一个lam_写给大忙人的se8

Nexus创建仓库、上传文件、查看列表_nexus制品列表查询树状展开接口-程序员宅基地

Ⅰ.通过仓库名获取文件列表import requestsimport json# 文件列表 参数 仓库名称 返回code 200# 请求方式: GETurl = '192.168.1.111:8081'auth = ("admin", "admin123")res = requests.get( url='http://{0}/service/rest/v1/components'.format(url), params={"repository": "test"}, _nexus制品列表查询树状展开接口

随便推点

7-2 两个有序链表序列的合并 (20 分)-程序员宅基地

已知两个非降序链表序列S1与S2,设计函数构造出S1与S2合并后的新的非降序链表S3。输入格式:输入分两行,分别在每行给出由若干个正整数构成的非降序序列,用−表示序列的结尾(−不属于这个序列)。数字用空格间隔。输出格式:在一行中输出合并后新的非降序链表,数字间用空格分开,结尾不能有多余空格;若新链表为空,输出NULL。输入样例:1 3 5 -12 4 6 ..._7-2 两个有序链表序列的合并

Oracle Gateway-程序员宅基地

# This is a customized agent init file that contains the HS parameters# that are needed for the Database...

OkHttp3使用介绍_使用浏览器访问https://txd.m.taobao.com/app/locallife/tbmc-程序员宅基地

首先引入okhttp框架 compile 'com.squareup.okhttp3:okhttp:3.7.0' compile 'com.squareup.okio:okio:1.13.0'GET请求private void demo1() { try { //get请求的参数拼在url后,需要编码,同时服务器也需要解码 String url ="http:..._使用浏览器访问https://txd.m.taobao.com/app/locallife/tbmc-app-home/shop-

计算机网络项目作业,2021计算机网络形考作业一(附答案)-程序员宅基地

2021计算机网络形考作业一(附答案) 计算机网络形考作业一(附答案) 一、选择题 题目1 计算机网络的功能有()。选择一项: A. 用户管理 B. 站点管理 C. 病毒管理 D. 资源共享 题目分析: 计算机网络的功能有:(1)资源共享;(2)数据通信;(3)集中管理;(4)增加可靠性;(5)提高系统的处理能力和安全功能。其中,资源共享和数据通信是计算机网络最基本的两大功能。 题目2 网络资源子..._计算机网络项目作业

springboot project报红解决办法_springboot propenties 文件 爆红_sicheng19961207的博客-程序员宅基地

当我们建立springboot的项目的时候,通常第一步导入依赖这时你有可能遇到projec报红这个问题主要是你下面的某个依赖没有引入成功因为maven库找不到对应的版本如果你没有给这个依赖加上version,建议加上,如果加上了建议换成更稳定的版本..._springboot propenties 文件 爆红

java 怎么把日期格式化时间_java 日期格式化-程序员宅基地

DateFormatDateFormat 是日期/时间格式化子类的抽象类,它以与语言无关的方式格式化并解析日期或时间。SimpleDateFormatSimpleDateFormat是一个以与语言环境有关的方式来格式化和解析日期的具体类。DateTimeFormatter 对 LocalDateTime进行格式化Date date = newDate();date.setYear(118);..._tue sep 04 23:13:40 cst 2018 java 格式化

推荐文章

热门文章

相关标签