python学习【八】 异常处理与反射
异常处理
一、异常基础
异常是开发人员在开发过程中遇到的错误,为了增加用户友好性,不让错误信息直接展现给用户,因此开发人员需要对这些错误进行捕获处理。
异常处理的基本结构如下:
try:
# 会出现异常的语句
export Exception,e:
# 对异常进行处理的语句
实例:将用户输入的两个数字相加
while True:
num1 = raw_input('num1:')
num2 = raw_input('num2:')
try:
num1 = int(num1)
num2 = int(num2)
result = num1 + num2
except Exception, e:
print '出现异常,信息如下:'
print e
当输入的内容不是数字时,异常将会触发。
例如:执行上面的代码,分别输入1和s,执行结构如下:
num1:1 num2:s 出现异常,信息如下: invalid literal for int() with base 10: 's'
二、异常种类
python为我们提供了很多异常的类型,用于处理开发中遇到的各种情况。
常用的异常类型如下:
AttributeError 试图访问一个对象没有的树形,比如foo.x,但是foo没有属性x IOError 输入/输出异常;基本上是无法打开文件 ImportError 无法引入模块或包;基本上是路径问题或名称错误 IndentationError 语法错误(的子类) ;代码没有正确对齐 IndexError 下标索引超出序列边界,比如当x只有三个元素,却试图访问x[5] KeyError 试图访问字典里不存在的键 KeyboardInterrupt Ctrl+C被按下 NameError 使用一个还未被赋予对象的变量 SyntaxError Python代码非法,代码不能编译(个人认为这是语法错误,写错了) TypeError 传入对象类型与要求的不符合 UnboundLocalError 试图访问一个还未被设置的局部变量,基本上是由于另有一个同名的全局变量, 导致你以为正在访问它 ValueError 传入一个调用者不期望的值,即使值的类型是正确的
现在我们已经知道异常的种类了,那么我们就可以在开发中对一些已知的异常进行处理。
例如:使用open()打开指定文件,当指定的文件不存在时,就会报IOError。这样我们在写程序时就可以指定对该异常进行捕获处理。代码如下:
>>> try:
... open("abc.txt",'r')
... except IOError:
... print "文件不存在"
但是有时候我们在写程序时可能会遇到有些异常是预知的有些是未知的,这样我们就需要使用下面这种方法来处理:
try:
dic = {'k1': 'v1'}
print dic['k2']
except KeyError,e:
print e
except Exception,e:
print e
先处理预知的异常,最后使用Exception捕获未知的异常。因为Exception是所有异常的基类,又称为万能异常。
这里你可能会有疑惑,为什么有万能异常,还要用其他的异常呢?其实,在开发过程我们有时候需要对不同的异常做不同的处理,如果只使用万能异常Exception的话,就无法实现这一要求。
异常的抛出机制:
1、如果在运行时发生异常,解释器会查找相应的处理语句(称为handler).
2、要是在当前函数里没有找到的话,它会将异常传递给上层的调用函数,看看那里能不能处理。
3、如果在最外层(全局“main”)还是没有找到的话,解释器就会退出,同时打印出traceback以便让用户找到错误产生的原因。
三、异常的复杂结构
try:
# 会出现异常的语句
export Exception,e:
# 对异常进行处理的语句
else:
语句块 # 如果在try子句执行时没有发生异常,则执行该语句块
finally:
语句块 # 不管有没有异常出现都执行该语句块
四、主动触发异常
raise关键字可以主动抛出一个异常。例如:
#coding=utf-8
filename = raw_input('please input file name:')
if filename=='hello':
raise NameError('input file name error !')
程序要求用户输入一个文件名,如果用户输入的文件名是hello ,那么抛出一个NameError的异常,用户输入hello 和NameError异常之间没有任何必然联系,只是人为的通过raise来这样定义,当然也可以定义称TypeError ,但是这里定义的异常类型必须是python提供的。
五、自定义异常
尽管内建的异常类型已经包括了大部分的情况,而且对于很多要求都已经足够了,但有些时候还是需要创建自己的异常类。
异常类的定义和创建普通类一样,但必须继承Exception类。例如:
class MyError(Exception):
def __init__(self, value):
self.value = value
def __str__(self):
return repr(self.value)
try:
raise MyError(2*2)
except MyError, e:
print 'My exception occurred, value:', e.value
反射
反射功能是由hasattr、getattr、setattr、delattr四个内置函数提供的。
现在有如下需求:
模拟web请求,当用户访问home/index时,返回index页面的结果,访问home/dev时,返回dev页面的结果。现在假如有五个页面时,在没有学习反射时,实现方式如下:
home.py #假设该模块中就是我们请求的页面
#!/usr/bin/env python # _*_ coding:utf-8 _*_ def index(): return 'result is home.index' def dev(): return 'result is home.dev' def descover(): return 'result is home.descover' def dev2(): return 'result is home.dev' def descover2(): return 'result is home.descover'
index.py
import home print 'oldboy....' url = raw_input('url:') if url == 'home/index': res= home.index() print res elif url == 'home/dev': res= home.dev() print res elif url == 'home/descover': res= home.descover() print res elif url == 'home/dev2': res= home.dev2() print res elif url == 'home/descover2': res= home.descover2() print res else: print '404 %s页面无法找到' %url
当我们的页面增加到更多时,这种写法就很low。而反射却正好可以解决这一问题。
反射实现代码如下:
import homeprint 'oldboy....'url = raw_input('url:')controller,action = url.split('/')res = getattr(home,action)print res()
通过上面两种实现方式的比较就可以看出反射功能的强大。
下面讲一下反射的四个内容函数的使用:
class Foo(object):
def __init__(self):
self.name = 'andy'
def func(self):
return 'func'
obj = Foo()
hasattr():
检查某容器中是否有某成员。例如:
print hasattr(obj, 'name') print hasattr(obj, 'func')
结果:
True
True
getattr()
从某容器中获取成员。例如:
print getattr(obj, 'name') func = getattr(obj, 'func')
print func()
结果:
andy
func
setattr()
设置成员到某容器中。例如:
设置前obj中内容如下: ['__class__', '__delattr__', '__dict__', '__doc__', '__format__', '__getattribute__', '__hash__', '__init__', '__module__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', 'func', 'name'] 执行setattr: setattr(obj, 'age', 20) setattr(obj, 'say','hello' )
设置后:
['__class__', '__delattr__', '__dict__', '__doc__', '__format__', '__getattribute__', '__hash__', '__init__', '__module__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', 'age', 'func', 'name', 'say']
delattr()
从某容器中删除成员。例如:
删除前: ['__class__', '__delattr__', '__dict__', '__doc__', '__format__', '__getattribute__', '__hash__', '__init__', '__module__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', 'age', 'func', 'name', 'say'] 执行delattr: delattr(obj, 'name') delattr(obj, 'age') 删除后: ['__class__', '__delattr__', '__dict__', '__doc__', '__format__', '__getattribute__', '__hash__', '__init__', '__module__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', 'func', 'say']
setattr和delattr操作都只是对内存中数据的操作,不影响原对象。

浙公网安备 33010602011771号