异常

python用异常对象来代表异常情况
如果异常对象未被处理或捕捉,程序就会用回溯(traceback,一种错误信息)来终止执行
而每个异常都是某些类的实例,可以引发,也可以捕捉

引发异常

可以用一个类(Exception的子类)或者实例参数调用raise语句
若使用的是类,那么程序会自动创建类的一个实例
重要的内建异常类有——

  • Exception:所有异常的基类
  • AttributeError:特性引用失败 或 赋值失败
  • IOError:试图打开不存在的文件等
  • IndexError:使用序列中不存在的索引
  • KeyError:使用映射中不存在的键
  • NameError:找不到名字(变量)
  • SyntaxError:代码错误
  • TypeError:内建操作或函数用于错误类型的对象
  • ValueError:内建操作或函数用于正确类型的对象,但使用不合适的值
  • ZeroDivisionError:除法或模除操作的第二参数为0
In [3]:
raise Exception
---------------------------------------------------------------------------
Exception                                 Traceback (most recent call last)
<ipython-input-3-fca2ab0ca76b> in <module>()
----> 1 raise Exception

Exception: 
In [4]:
raise Exception('Error!  Error!')
---------------------------------------------------------------------------
Exception                                 Traceback (most recent call last)
<ipython-input-4-4d6c6ba4cc4b> in <module>()
----> 1 raise Exception('Error!  Error!')

Exception: Error!  Error!
In [5]:
#创建异常类——只需要让该类继承Exception即可
class SomCustomException(Exception):
    pass

捕捉异常

利用try/except语句
try:
......
except 异常类:
......

In [6]:
try:
   x = input('被除数')
   y = input('除数') 
   print x/y
except ZeroDivisionError:
    print "The second number can'n be zero"
被除数2
除数0
The second number can'n be zero

In [7]:
2/0
---------------------------------------------------------------------------
ZeroDivisionError                         Traceback (most recent call last)
<ipython-input-7-897f73788bb0> in <module>()
----> 1 2/0

ZeroDivisionError: integer division or modulo by zero
In [8]:
#捕捉到异常后,可以用不带参数的raise语句重新引发异常(传递异常)
In [9]:
class MuffledCalculator:
    '设计一个具有屏蔽错误信息的计算器类'
    muffled = False     #屏蔽机制的开关,默认关闭
    def calc(self, expr):
        try:
            return eval(expr)        #通过eval函数执行expr求值字符串
        except ZeroDivisionError:
            if self.muffled:
                print "Division by zero is illegal"
            else:
                raise       #使用不带参数的raise语句,重新引发异常
In [10]:
calculator = MuffledCalculator()
In [11]:
calculator.calc('10/2')
Out[11]:
5
In [12]:
calculator.calc('10/0')       #关闭屏蔽机制的情况下
---------------------------------------------------------------------------
ZeroDivisionError                         Traceback (most recent call last)
<ipython-input-12-a3308c220d5e> in <module>()
----> 1 calculator.calc('10/0')

<ipython-input-9-f40f09de667e> in calc(self, expr)
      4     def calc(self, expr):
      5         try:
----> 6             return eval(expr)        #通过eval函数执行expr求值字符串
      7         except ZeroDivisionError:
      8             if self.muffled:

<string> in <module>()

ZeroDivisionError: integer division or modulo by zero
In [13]:
calculator.muffled = True       #打开屏蔽机制
In [14]:
calculator.calc('10/0')
Division by zero is illegal

except子句可以有多个,用不同方式来处理多种异常
一个except子句的异常类可以有多个,用元组包含起来即可,用同一种方式来处理多种异常

In [15]:
#一个except子句含有多个异常类
try:
    x = input('First number: ')
    y = input('Second number: ')
    print x/y
except (ZeroDivisionError, TypeError, NameError):
    print 'Your numbers were bogus...'
First number: 10
Second number: '2'
Your numbers were bogus...

捕捉对象

python2中可以把对象作为except子句的第二个参数
python3中可以把except子句写成except ...... as ...
若只有一个异常,捕捉的对象作为单独的变量
若有多个异常,捕捉的对象会整合到一个元组里

In [1]:
try:
    x = input('First number: ')
    y = input('Second number: ')
    print x/y
except (ZeroDivisionError, TypeError) as e:      #(python3)
#except (ZeroDivisionError, TypeError), e:  (python2)
    print e         #捕捉的对象可以用print打印出来
First number: 10
Second number: 0
integer division or modulo by zero

In [2]:
e
Out[2]:
ZeroDivisionError('integer division or modulo by zero')

真正的全捕捉

使用不带参数的except语句可以捕捉其余的所有异常

In [3]:
try:
    x = input('First number: ')
    y = input('Second number: ')
    print x/y
except ZeroDivisionError:
    print 'Error   Division   Zero'
except:
    print 'Something wrong happened'
First number: 10
Second number: 
Something wrong happened

In [6]:
#事实上,不带参数的except只能捕捉Exception类的异常

如果正常执行了

我们还可以给它加上else子句

In [4]:
try:
    print 'A simple task'
except:
    print 'What? Something went wrong?'
else:
    print 'Ah...It went as planned'
A simple task
Ah...It went as planned

In [5]:
#这里可以和循环搭配
#不断捕捉异常,直到输入正确为止
#就把try语句写到循环While True内,在else子句写上break即可

最后……

finally子句:在try一系列代码块的最后执行
用于对可能的异常进行清理

In [7]:
x = None
In [8]:
try:
    x = 1/0
finally:
    print 'Cleaning up...'
    del x
Cleaning up...

---------------------------------------------------------------------------
ZeroDivisionError                         Traceback (most recent call last)
<ipython-input-8-97829083c8de> in <module>()
      1 try:
----> 2     x = 1/0
      3 finally:
      4     print 'Cleaning up...'
      5     del x

ZeroDivisionError: integer division or modulo by zero
In [9]:
x
---------------------------------------------------------------------------
NameError                                 Traceback (most recent call last)
<ipython-input-9-401b30e3b8b5> in <module>()
----> 1 x

NameError: name 'x' is not defined
In [10]:
#出错后,x变量被删除了
#finally子句更多的是用于关闭文件  或者  网络套接字

异常的“传播”

假设有两个函数a和b,a函数的定义内又调用了b函数
当我们调用a函数时,在执行b函数的过程中出错
如果在b函数中没有处理异常,那么它会传播到a函数中
如果在a函数中没有处理异常,那么它会传播到主程序中
如果主程序也没有处理异常,那么程序会出错导致带着栈跟踪终止

try/except和if/else

try/except语句来处理可能遇到的错误更加自然和高效率

In [11]:
#效率比较——尝试访问一个不一定存在的键
In [13]:
person = {'name': 'Zk', 'age': 21,'Occupation':'Student'}
In [19]:
#if/else语句实现
def decribePerson(person):
    print 'Decription of', person['name']
    print 'Age:', person['age']
    if 'Occupation' in person:
        print 'Occuption:', person['Occupation']


In [20]:
decribePerson(person)
Decription of Zk
Age: 21
Occuption: Student

In [21]:
#此处如果Occpution存在,那么这个键会被访问两次(if中和print中)
In [22]:
#try/except语句实现
def decribePerson(person):
    print 'Decription of', person['name']
    print 'Age:', person['age']
    try:
        print 'Occuption:', person['Occupation']
    except KeyError:
        pass
In [23]:
decribePerson(person)
Decription of Zk
Age: 21
Occuption: Student

In [24]:
#此处如果Occupation存在,那么这个键只会被访问一次(print中)
In [24]:
 
In [25]:
#查看对象特性
In [26]:
class writer():
    write = True
In [27]:
obj = writer()
In [29]:
try:
    obj.write       #尝试去访问这个特性
except AttributeError:
    print 'The object is not writeable'
else:
    print 'The object is writeable'
The object is writeable

In []: