21.错误和异常

 

 

 

 

 

本章主题

1、什么是异常

2、Python中的异常

3、探测和处理异常

4、上下文管理

5、引发异常

6、断言

7、标准异常

8、创建异常

9、相关模块

 

 

 

一、什么是异常

错误

1、从软件方面来说,错误是语法或是逻辑上的

2、语法错误指示软件的结构上有错误,导致不能被解释器解释或编译器无法编译。这些错误必须在程序执行前纠正

3、 逻辑错误可能是由于不完整或是不合法的输入所致。还可能是逻辑无法生成、计算,或是输出结果需要的过程无法执行 ;

 4、这些错误通常分别被称为域错误和范围错误

 

 

异常

1、当python检测到一个错误时,解释器就会指出当前流已经无法继续执行下去,这时候就出现了异常

2、异常是因为程序出现了错误而在正常控制流以外采取的行为

3、这个行为又分为两个阶段:

  3.1 首先是引起异常发生的错误

  3.2 然后是检测(和采取可能的措施)阶段 

 

4、第一个阶段是在发生了一个异常条件(有时候也叫做例外的条件)后发生的.只要检测到错误并且意识到异常条件, 解释器会引发一个异常

5、引发也可以叫做触发, 引发或者生成

6、对异常的处理发生在第二阶段。异常引发后, 可以调用很多不同的操作


 

 

 

二、python中的异常

1、当程序运行时,因为遇到未解的错误而导致中止运行,便会出现traceback消息,打印异常

 

 三、检测和处理异常


1、异常可以通过 try 语句来检测. 任何在 try 语句块里的代码都会被监测, 检查有无异常发生
2、try 语句有两种主要形式: try-except 和 try-finally 。
3、这两个语句是互斥的, 也就是说你只能使用其中的一种. 一个 try 语句可以对应一个或多个 except 子句, 但只能对应一个finally 子句,
或是一个 try-except-finally 复合语句
4、你可以使用 try-except 语句检测和处理异常. 你也可以添加一个可选的 else 子句处理没有探测到异常的时执行的代码.
5、而 try-finally 只允许检测异常并做一些必要的清除工作(无论发生错误与否), 没有任何异常处理设施.

 

 

try-except语句

1、try-except 语句(以及其更复杂的形式)定义了进行异常监控的一段代码, 并且提供了处理异常的机制
2、最常见的try-except 语句语法如下所示. 它由try块和except块(try_suite 和except_suite )组成, 也可以有一个可选的错误原因

 

try:
  try_suite #监控这里的异常
except Exception[, reason]:   except_suite #异常处理代码

 例子:

 

try:
    open('foo.txt')
except IOError:
    print 'no such file'

 

执行结果:

no such file

 

 

带有多个except的try语句

1、可以把多个except语句连接在一起,处理一个try块中可能发生的多种异常

语法:

except Exception1[, reason1]:
  suite_for_exception_Exception1
except Exception2[, reason2]:
  suite_for_exception_Exception2
  :

 

2、首先尝试执行 try 子句, 如果没有错误, 忽略所有的 except 从句继续执行

3、如果发生异常, 解释器将在这一串处理器(except 子句)中查找匹配的异常. 如果找到对应的处理器,执行流将跳转到对应的处理器那里

例子:

#!/usr/bin/env python
#coding:utf-8
try:
    data = int(raw_input('input a number: '))
except KeyboardInterrupt:
    print 'user cancelled'
except ValueError:
    print 'you must input a number!'

执行结果:

input a number: hello
you must input a number!

 

 

处理多个异常的 except 语句


1、可以在一个 except 子句里处理多个异常. except 语句在处理多个异常时要求异常被放在一个元组里:
2、语法:

except (Exc1[, Exc2[, ... ExcN]])[, reason]:
  suite_for_exceptions_Exc1_to_ExcN

 

 

 捕获所有异常

1、如果出现的异常没有出现在指定要捕获的异常列表中,程序仍然会中断

2、在异常继承的树结构中,BaseException是在最顶层的,所以使用它可以捕获任意类型的异常

try:
    data = int(raw_input('input a number: '))
except BaseException:
    print '\nsome error'

 

执行结果:

input a number: [Ctrl + C]
some error

 

异常参数

1、异常也可以有参数,异常引发后它会被传递给异常处理器

2、当异常被引发后,参数是作为附加帮助信息传递给异常处理器的

3、虽然异常原因是可选的, 但标准内建异常提供至少一个参数, 指示异常原因的一个字符串.
4、异常的参数可以在处理器里忽略, 但 Python 提供了保存这个值的语法
5、要想访问提供的异常原因, 你必须保留一个变量来保存这个参数. 把这个参数放在except 语句后, 接在要处理的异常后面. except 语句的这个语法可以被扩展为 :

# single exception
except Exception[, reason]:
    suite_for_Exception_with_Argument

# multiple exceptions
except (Exception1, Exception2, ..., ExceptionN)[, reason]:
  suite_for_Exception1_to_ExceptionN_with_Argument

6、reason 将会是一个包含来自导致异常的代码的诊断信息的类实例. 异常参数自身会组成一个元组,并存储为类实例(异常类的实例)的属性.

 

例子:

 

try:
    data = 10 / 0
except ZeroDivisionError, e:
    print 'Error:', e

执行结果:

Error: integer division or modulo by zero

 

 

 

else子句

1、在try范围中没有异常被检测到时,执行else子句

2、在else范围中的任何代码运行前,try范围中的所有代码必须完全成功(也就是,结束前没有引发异常)

 

 

#!/usr/bin/env python
#coding:utf-8

try:
    res = 10 / int(raw_input('input a number: '))

except BaseException, e:
    print 'Error:', e

else:
    print res

执行结果:

input a number: 5
2

 

 

finally子句

1、finally子句是无论异常是否发生,是否捕捉都会执行的一段代码

2、你可以将 finally 仅仅配合try 一起使用,也可以和 try-except(else 也是可选的)一起使用

2、如果打开文件后,因为发生异常导致文件没有关闭,可能会发生数据损坏。使用finally可以保证文件总是能正常的关闭

 

try:
  try:
    ccfile = open('carddata.txt', 'r')
    txns = ccfile.readlines()
  except IOError:
    log.write('no txns this month\n')
finally:
  if ccfile:
    ccfile.close()

 

try-finally语句

1、另一种使用 finally 的方式是 finally 单独和 try 连用

2、.这个 try-finally 语句和 try-except区别在于它不是用来捕捉异常的.

3、作为替代,它常常用来维持一致的行为而无论异常是否发生.我们得知无论 try 中是否有异常触发,finally 代码段都会被执行

try:
  try_suite
finally:
  finally_suite #无论如何都执行

4、当在 try 范围中产生一个异常时,(这里)会立即跳转到 finally 语句段.当 finally 中的所有代码都执行完毕后,会继续向上一层引发异常.
5、因而常常看到嵌套在 try-except 中的 try-finally 语句

 

try-except-else-finally:厨房一锅端

1、我们所见过的所有不同的可以处理异常的语法样式:

try:
  try_suite
except Exception1:   suite_for_Exception1
except (Exception2, Exception3, Exception4):   suite_for_Exceptions_2_3_and_4
except Exception5, Argument5:   suite_for_Exception5_plus_argument
except (Exception6, Exception7), Argument67:   suite_for_Exceptions6_and_7_plus_argument
except:   suite_for_all_other_exceptions else:   no_exceptions_detected_suite finally:   always_execute_suite

 

 

 

四、触发异常

 

1、要想引发异常,最简单的形式就是输入关键字raise,后面跟要引发的异常的名称

2、执行raise语句时,Python会创建指定的异常类的一个对象

3、raise语句还可指定对异常对象进行初始化的参数

 

#!/usr/bin/env python
#coding:utf-8

number = 3
try:
    if number < 10:
        raise ValueError, 'Number is too smalle.'
except ValueError,e:
    print 'Error:', e

 

执行结果:

Error: Number is too smalle.

 

 

raise语句

 

语法与惯用法

1.raise 语句对所支持是参数十分灵活,对应到语法上就是支持许多不同的格式.rasie 一般的用法是:

raise [SomeException [, args [, traceback]]]

2.第一个参数,SomeExcpetion,是触发异常的名字.如果有,它必须是一个字符串,类或实例
3.如果有其他参数(arg 或 traceback),就必须提供 SomeExcpetion
4.第二个符号为可选的 args(比如参数,值),来传给异常;这可以是一个单独的对象也可以是一个对象的元组 ;当异常发生时,异常的参数总是作为一个元组传入 ;
如果 args 原本就是元组,那么就将其传给异常去处理;如果 args 是一个单独的对象,就生成只有一个元素的元组(就是单元素元组)

5.最后一项参数,traceback,同样是可选的(实际上很少用它),如果有的话,则是当异常触发时新生成的一个用于异常-正常化(exception—normally)的追踪(traceback)对象.当你想重新引发异常时,第三个参数很有用(可以用来区分先前和当前的位置).如果没有这个参数,就填写 None.

 

五、断言

1、断言是一句必须等价于布尔值为真的判定

2、此外,发生异常也意味着表达式为假

3、断言可以简简单单的想象为 raise-if 语句(更准确的说是raise-if-not 语句).测试一个表达式,如果返回值是假,触发异常

4、如果断言成功不采取任何措施(类似语句),就会触发AssertionError(断言错误)的异常

5、assert 的语法如下: assert expression[, arguments]

number = 3
try:
    assert number > 10, 'number is too small'
except AssertionError, e:
    print 'Error:', e

执行结果:

Error: number is too small

 

 

 

六、标准异常

1、所有的标准/内建异常都是从根异常派生的
2、目前,有 3 个直接从 BaseException 派生的异常子类:SystemExit,KeyboardInterrupt 和 Exception
3、其他的所有的内建异常都是 Exception 的子类
4、到了 Python2.5,所有的异常的都是新式类,并且最终都是 BaseException 的子类.
5、在这一版中,SystemExit 和 KeyboardInterrupt 从 Exception 的继承中移到 BaseException 的继承下.这样可以允许如 except Exception 的语句捕获所有非控制程序退出的异常
6、从 Python1.5 到 Python2.4.x,异常是标准的类,在这之前,他们是字符串
7、从 Python2.5 开始,不再支持构建基于字符串的异常并且被正式的弃用,也就是说你不能再触发一个字符串异常了.在 2.6,你将不能捕获他们
8、还有一个要求就是所有新的异常最终都是 BaseException 的子类,以便于他们有一个统一的接口

 

BaseException 所有异常的基类

SystemExitb python 解释器请求退出

KeyboardInterrupt  用户中断执行(通常是输入^C)
Exception 常规错误的基类
StopIteration 迭代器没有更多的值 GeneratorExit 生成器(generator)发生异常来通知退出 SystemExith Python 解释器请求退出 StandardError 所有的内建标准异常的基类 ArithmeticError 所有数值计算错误的基类 FloatingPointError 浮点计算错误 OverflowError 数值运算超出最大限制 ZeroDivisionError 除(或取模)零 (所有数据类型) AssertionErrord 断言语句失败 AttributeError 对象没有这个属性 EOFError 没有内建输入,到达 EOF 标记 EnvironmentError 操作系统错误的基类 IOError 输入/输出操作失败 OSError 操作系统错误 WindowsErrorh Windows 系统调用失败 ImportError 导入模块/对象失败 KeyboardInterrupt 用户中断执行(通常是输入^C) LookupError 无效数据查询的基类 IndexError 序列中没有没有此索引(index) KeyError 映射中没有这个键 MemoryError 内存溢出错误(对于 Python 解释器不是致命的) NameError 未声明/初始化对象 (没有属性) UnboundLocalErrorh 访问未初始化的本地变量 ReferenceError 弱引用(Weak reference)试图访问已经垃圾回收了的对象 RuntimeError 一般的运行时错误 NotImplementedError 尚未实现的方法 SyntaxError Python 语法错误 IndentationError 缩进错误 TabErrorg Tab 和空格混用 SystemError 一般的解释器系统错误 TypeError 对类型无效的操作 ValueError 传入无效的参数 UnicodeErrorh Unicode 相关的错误 UnicodeDecodeError Unicode 解码时的错误 UnicodeEncodeError Unicode 编码时错误
UnicodeTranslateError Unicode 转换时错误
Warning 警告的基类
DeprecationWarning 关于被弃用的特征的警告
FutureWarning 关于构造将来语义会有改变的警告
OverflowWarning 旧的关于自动提升为长整型(long)的警告
PendingDeprecationWarning 关于特性将会被废弃的警告
RuntimeWarning 可疑的运行时行为(runtime behavior)的警告
SyntaxWarning 可疑的语法的警告
UserWarning 用户代码生成的警告

 

七、创建异常

1、尽管标准异常集包含的内容已经相当广泛,你还是可以创建自己的异常

2、.一种情况是你想在特定的标准异常和模块异常中添加额外的信息

 

八、异常和 sys 模块

1、另一种获取异常信息的途径是通过 sys 模块中 exc_info()函数. 此功能提供了一个 3 元组(3-tuple)的信息, 多于我们单纯用异常参数所能获得

2、让我们看看如何用 sys.exc_info() :

#!/usr/bin/env python
#coding:utf-8

try:
    float('abc123')
except:
     import sys
     exc_tuple = sys.exc_info()

print exc_tuple

for eachItem in exc_tuple:
    print eachItem

 

执行结果:

(<type 'exceptions.ValueError'>, ValueError('could not convert string to float: abc123',), <traceback object at 0x04FD1D28>)
<type 'exceptions.ValueError'>
could not convert string to float: abc123
<traceback object at 0x04FD1D28>

 

 

从 sys.exc_info()得到的元组中是:
1、exc_type: 异常类
2、exc_value: 异常类的实例
3、exc_traceback: 追踪(traceback)对象

我们所熟悉的前两项:实际的异常类, 和这个异常类的实例(和在上一节我们讨论的异常参数是一样的) .

第三项, 是一个新增的追踪(traceback)对象. 这一对象提供了的发生异常的上下文.
它包含诸如代码的执行帧,异常发生时的行号等信息

 

注意:

在旧版本中的 Python 中, 这三个值分别存在于 sys 模块, 为 sys.exc_type , sys.exc_value ,sys.exc_traceback .

不幸的是, 这三者是全局变量而不是线程安全的. 我们建议亡羊补牢, 用sys.exc_info()来代替.

在未来版本 Python 中,所有这三个变量都将被逐步停用,并最终移除

 

 

例子:

timer.py内容:

#!/usr/bin/env python
#coding:utf8

import time 

for i in range(1,11):
    print i
    try:
        time.sleep(1)
    except KeyboardInterrupt:
        pass 
    
print 'done'

 

 

 

game.py内容:

#!/usr/bin/env python
#coding:utf8

import random
import sys

ch_list = ["石头","剪刀",""]
win_list = [["石头","剪刀"],["剪刀",""],["","石头"]]


prompt = """(0)石头
(1)剪刀
(2)布
请选择(012):"""

try:
    ind = int(raw_input(prompt))
    player = ch_list[ind]
except (KeyboardInterrupt,EOFError):
    print '\nBye Bye'
    sys.exit(1)

except (ValueError,IndexError):
    print "\nInvalid input.."
    sys.exit(2)
    
computer = random.choice(ch_list)
print "Your choice : %s,Computer choice: %s" %(player,computer)
if [player,computer] in win_list:
    print '\033[31;1mYou Win!!!\033[0m'
elif player == computer:
    print '\033[32;1m平局\033[0m'
else:
    print "\033[31;You Lose\033[0m"

 

执行结果:

(0)石头
(1)剪刀
(2)布
请选择(012):3

Invalid input..

 

division1.py的内容:

 

#!/usr/bin/env python
#coding:utf8


try:
    num = int(raw_input("number: "))
    result = 100 / num
except (KeyboardInterrupt,EOFError):
    print "User cancelled"
    
except (ValueError,ZeroDivisionError):
    print "Invalid input"

 

division2.py的内容:

#!/usr/bin/env python
#coding:utf8


try:
    num = int(raw_input("number: "))
    result = 100 / num
except (KeyboardInterrupt,EOFError):
    print "User cancelled"
    
except (ValueError,ZeroDivisionError),e:
    print "Error: ",e

 

division3.py的内容:

#!/usr/bin/env python
#coding:utf8


try:
    num = int(raw_input("number: "))
    result = 100 / num
except (KeyboardInterrupt,EOFError):
    print "User cancelled"
    
except (ValueError,ZeroDivisionError),e:
    print "Error: ",e

else:
    print result

 

division4.py的内容:

 

#!/usr/bin/env python
#coding:utf8


try:
    num = int(raw_input("number: "))
    result = 100 / num
except (KeyboardInterrupt,EOFError):
    print "User cancelled"
    
except (ValueError,ZeroDivisionError),e:
    print "Error: ",e

else:
    print result
    
finally:
    print 'done'

 

age1.py的内容:

#!/usr/bin/env python
#coding:utf8

def set_age(name,age):
    if age < 1 or age > 150:
        raise ValueError,"Age out of range"
    print "%s is %s years old ." % (name,age)
    
set_age('bob', 200)

 

age2.py的内容:

#!/usr/bin/env python
#coding:utf8

def set_age(name,age):
    if age < 1 or age > 150:
        raise ValueError,"Age out of range"
    print "%s is %s years old ." % (name,age)
 
def set_age2(name,age):
    assert 0 < age <= 150,"Age out of range"

    print "%s is %s years old ." % (name,age) 
   
set_age2('bob', 20)
set_age2('bob', 200)

 

u2d2.py内容:

#!/usr/bin/env python
#coding:utf8

import os 
import sys

def unix2dos(fname):
    dfname = os.path.splitext(fname)[0] + '.win'
    with open(fname) as src_fobj:
        with open(dfname,'w') as dst_fobj: 
            for line in src_fobj:
                dst_fobj.write(line.rstrip('\n\r') + '\r\n')
        
    
if __name__ == '__main__':
    try:    
        filename = sys.argv[1]
    except IndexError:
        print "Usage: %s filename " % sys.argv[0]
        sys.exit(1)
    if not os.path.isfile(filename):
        print "No such file: %s" % filename 
        sys.exit(2)
    unix2dos(filename)

 

posted @ 2019-06-18 23:18  钟桂耀  阅读(278)  评论(0编辑  收藏  举报