1.前言
经历了一段论文的挣扎以后,我胡汉三又回来了。还是好汉一条,接着上次的面向对象的话题,本章来介绍一个简单的话题--异常处理。编写程序完成后,我们需要编译和连接,在这个编译器会给我们抛出一些错误和异常,我们通常会将这些错误和异常分类。再去具体的程序里面检查到底是哪里出现了错误。
一般常见的处理方式是:我们在可能会发生错误和异常的地方用条件语句先去判断,但是这样的做法只是暂时的。假如你的程序设计的很庞大、结构很复杂。条件语句的多次出现会让程序很不灵活,而且会加大程序阅读的难度。那么,我们应该怎样处理呢?一起来看看。
2.异常是啥?
Python用异常对象来表示异常对象,当遇到错误后,会引发异常。如果这个异常没有被处理,那么就会回溯终止执行。来看看一个具体的列子:
>>> 1/0
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
ZeroDivisionError: division by zero
我们知道,在除法运算分母当然不能是0了,这时候Python就会抛出一个异常。ZeroDivisionError:division by zero。其实,每个异常都是一些类,在这个例子里面我们看到就是ZeroDivisionError的实例。这些例子可以被引发,并且可以通过多种方式和方法,并不仅仅限于1/0这个例子。这样的异常可以让程序可以捕捉到这样的错误并及时的处理它,而不是说一旦发生这个错误就让整个程序崩溃!
3.异常详述
3.1属于自己的简单异常
处于人性化和易读性的考虑,Python内建了许多的异常类,我们可以使用raise语句和内建异常中的Exception来设置属于自己的异常提示语句。来看看下面的例子。
>>> raise Exception
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
Exception
>>> raise Exception("America is small than China")
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
Exception: America is small than China
在上面的例子中,起初我们并没有给定异常提醒语句,只是简单出发一个异常。第二次的时候,我们利用提示语句"America is small than China",果不其然,这样的提示被打印出来了。这样的方式提示着我们:在你不确定哪里有异常的时候,最好采用下面即将说道的异常处理方法,并设置好合适的提示语,这样程序设计出来才是合格的。By the Way,给大家一些Python内建的异常。
类名 | 描述 |
Exception | 所有异常的基类 |
AttributeError | 特性引用或者赋值失败时候引发 |
IOError | 试图打开不存在文件时候引发 |
IndexError | 在序列中引用不存在的索引时候引发 |
KeyError | 在使用映射中不存在的键的时候引发 |
NameError | 找不到名字(变量)时候引发 |
SyntanxError | 代码形式错误时候引发 |
TypeError | 内建操作或者函数应用于错误类型的对象时候引发 |
ValueError | 在内建操作或者函数应用于正确类型的对象,但是该对象使用不适合的值时候引发 |
ZeroDivisionError | 在除法或者模除操作第二个数为0 的时候引发 |
3.2捕捉异常并处理
在这部分,我们直接来看一个例子,会比较容易懂得异常处理的方法。
class MufflesCalculator:
muffled=False
def calc(self,expr):
try:
return eval(expr)
except ZeroDivisionError:
if self.muffled:
print 'Division by Zero is illegal'
else:
raise
calculator=MufflesCalculator()
print calculator.calc('10/2')
print calculator.calc('10/0')
/usr/local/bin/python2.7 /Users/yangjiayuan/PycharmProjects/day/day15/the_use_of_except.py
5
Traceback (most recent call last):
File "/Users/yangjiayuan/PycharmProjects/day/day15/the_use_of_except.py", line 16, in <module>
print calculator.calc('10/0')
File "/Users/yangjiayuan/PycharmProjects/day/day15/the_use_of_except.py", line 7, in calc
return eval(expr)
File "<string>", line 1, in <module>
ZeroDivisionError: integer division or modulo by zero
Process finished with exit code 1
看一看上面的运行结果,正常的分母理所当然得到的结果是正确的。但是当我们将分母改为0的时候,编译器报错了,虽然这个异常被捕捉到了但是并没有被“屏蔽”,而且已经被传递了。这是由于我们没有将屏蔽机制打开。再来看看下面这样的处理结果。
class MufflesCalculator:
muffled=True
def calc(self,expr):
try:
return eval(expr)
except ZeroDivisionError:
if self.muffled:
print 'Division by Zero is illegal'
else:
raise
calculator=MufflesCalculator()
print calculator.calc('10/2')
print calculator.calc('10/0')
/usr/local/bin/python2.7 /Users/yangjiayuan/PycharmProjects/day/day15/the_use_of_except.py
5
Division by Zero is illegal
None
Process finished with exit code 0
现在的结果是不是和上面的不一样了,注意看红色字体的部分和上面程序里的对比。这也是我们打开了“屏蔽”机制。屏蔽掉这样的异常,也就是不让异常传递下去。有人很奇怪为什么我将调用两次函数的运行结果打印出来,最后会出现一个None。如果除零行为发生,而且屏蔽机制打开的话,calc方法会隐式的返回一个None。如果屏蔽机制是开的,那么久不应依赖返回值。再来看看下面这样的写法。
def Validate(name,pwd):
if name=='alex' and pwd=='123':
return True
else:
return False
try:
res=Validate('alex','456')
if res:
print 'sucess login'
else:
raise Exception('failure login')
except Exception,e:
print 'code in database'
print e
还是注意红色字体标注的位置,如果你想再except语句中访问异常对象本身,可以使用两个参数,就算你想捕捉多个异常也只需要向except子句提供一个参数,一个元组。就像上面的写法一样。这是在Python2.x里面的写法,在Python3中会写成这个样子:except Exception as e。使用不同的编译器环境的话需要注意到这一点。讲到这里,我们再来看看下面的处理方式。
while True:
try:
x=int(raw_input('Enter the first number: '))
y=int(raw_input('Enter the second number: '))
value=x/y
print 'x/y is', value
except:
print 'Invaild input,Please try again'
else:
break
/usr/local/bin/python2.7 /Users/yangjiayuan/PycharmProjects/day/day15/the_use_of_except.py
Enter the first number: 100
Enter the second number: 0
Invaild input,Please try again
Enter the first number: 8
Enter the second number: lalala
Invaild input,Please try again
Enter the first number: 9
Enter the second number: hahaha
Invaild input,Please try again
Enter the first number: 1000
Enter the second number: 10
x/y is 100
Process finished with exit code 0
上面的这个程序做了进一步的改进,else语句出现了。循环只有在没有发生异常的时候才会跳出。仔细体会一个else在这里的作用。还有,你可以尝试将这里的异常捕捉方式换为except Exception,e这样的方式会更好!最后来看的例子很简单。
try:
1/0
except ZeroDivisionError:
print "Unknow variable."
else:
print "That went well."
finally:
print "Cleaning up."
try:
1/1
except ZeroDivisionError:
print "Unknow variable."
else:
print "That went well."
finally:
print "Cleaning up."
自己运行以下上面这两段代码,看看有什么结果。特别是关注finally语句的结果。
异常并不是很复杂。如果知道某段代码可能会导致某种异常,而又不希望程序以堆栈跟踪的形式终止,那么久根据需要天剑try/except或者try/finally语句进行处理。尽量不要使用if/else语句来代替try/except语句。很多情况下这样并不会提高效率。好了,异常的有关内容就讲到这里,下一章节预告--方法、属性、迭代器。