python——面向对象篇之异常和反射
- 内置函数isinstance和issubclass
1.1 isinstance用法:
1 isinstance(string,str)
判断第一个参数是否是第二个参数的子集,例如:1 print isinstance("test",str) #判断test是否是字符串类型 2 3 C:\Python27\python.exe D:/python/s11/8day/反射/test.py 4 5 True 6 7 8 print isinstance(123,int) #判断123是否是整型类型 9 10 C:\Python27\python.exe D:/python/s11/8day/反射/test.py 11 12 True
1.2 issubclass用法:
1 issubclass(sub, super)
判断sub是否是super的派生类,返回值也是布尔值
注:使用isinstance和issubclass会破坏对象的多态性 - 异常处理
python在遇到错误后,会引发异常,如果异常对象并未被处理或者捕捉,则程序就会用所谓的回溯(traceback)来终止程序的执行,如下所示:
1 C:\Python27\python.exe D:/python/s11/8day/反射/test.py 2 Traceback (most recent call last): 3 File "D:/python/s11/8day/test.py", line 26, in <module> 4 print isinstance(d,int) 5 NameError: name 'd' is not defined
在python里,每个异常都是类Exception的实例,Exception可以说是所有异常的基类。如果我们捉住错误并对其处理,那整个程序就不会回溯而是继续执行下去。
2.1 被动异常
即把代码写在try块里,当程序抛错时自动触发,except则负责捕捉和处理异常,用法如下1 try: 2 代码块 3 except Exception,e: #e是exception的实例,是一个对象;上面的错误信息都封装在e中 4 异常处理 #在这里你可以打印异常信息,如(print e);也可以做其他操作如把异常信息写入日志
在这需要注意的是:如果你print e的时候,因为正常来说print对象时返回的应该是对象的内存地址,而在异常处理这里,你print对象时会调用exception的__str__方法,将异常信息返回而不是返回内存地址,所以这里你print的时候会发现显示的会是异常信息,对此你不需要觉得奇怪。
在except捕获异常时,你还可以在except后面再加个except,就像自来水过滤一样一层层的捕获特定的异常1 try: 2 代码块 3 except KeyError,e: #试图访问字典里不存在的键 4 print e 5 except ValueError,e: #传入一个调用者不期望的值,即使值的类型是正确的 6 print e 7 except Exception,e: #如果你无法预期会发生什么异常,那好,我们还有个万能异常 8 print e
另一种写法:1 try: 2 Garen = Hero 3 except (KeyError,ValueError),e: #写成元组形式 4 print e
2.2 主动触发异常
不等程序代码遇到错误抛出后才触发,而是我们在预计会出现问题的地方提前触发1 try: 2 raise Exception('这里出现错误') #提前传参并实例化异常 3 except Exception,e: #把错误信息封装到exception的实例e里 4 print e
2.3 自定义异常
既是我们自己定义异常的触发条件和处理方法,创建自定义异常的时候需要直接或者间接的继承Exception1 class testError(exception): //继承exception异常基类 2 def __init__(self,msg=None): 3 self.msg = msg 4 def __str__(self): 5 if self.msg: //根据传入的参数动态显示异常信息 6 return self.msg 7 else: 8 return "自定义异常" //默认异常显示信息
调用自定义异常1 try: 2 raise testError() //实例化自定义异常类,后面括号可传入参数 3 except testError,e: 4 print e
2.4 finally
不管try子句是否发生异常都会执行,比如当你在读写文件或者scoket通讯时,不管是否出现异常都应该在结束后关闭文件或者网络套接字,这时就可以把关闭方法放在finally子句中1 try: 2 f = open("test.txt","a") 3 f.write(data) 4 except Exception,e: 5 print e 6 finally: 7 f.close()
1 try: 2 Garen = Hero 3 except: 4 pass
这是一种比较危险的用法,它会忽略程序的所有异常而继续的让程序执行下去。
异常处理只需要记住一点:当我们知道某段代码可能会导致某种异常,但我们又不希望程序停止,那么我们就可以根据需要添加异常处理。
扩展:反射中的getattr的内部实现也是通过访问特性并捕捉可引发的AttributeError异常区实现的 - 反射
反射,将名称转换成动词,这是笔者看完《Execution in the Kingdom of Nouns》后对反射的第一反应,大家有兴趣可以去翻阅,一篇不算太长的文章。
下面我们来想象一下这么一个场景:你坐在电脑旁,玩着LOL,通过鼠标和键盘传递信息,游戏里的英雄相应的做出操作,这就可以看成反射。这是怎么做到的呢?让我们往下看:
首先我们定义了一个英雄模板,也就是基类。
1 class Hero: 2 def __init__(self): 3 self.ATK = 30 4 self.DEF = 10 5 self.HP = 300 6 self.MP = 100 7 8 def walk(self): 9 print "你的英雄正走向目标" 10 def running(self): 11 print "你的英雄正跑向目标" 12 def skills(self): 13 print "你的英雄正在释放技能" 14 def attack(self): 15 print "你的英雄正在攻击"
上面这个英雄基类里有攻击、防御、生命和魔法值四个属性以及走路、跑步、释放技能和攻击四个方法动作。
现在你在电脑前创建了一个角色盖伦,程序开始把类实例化:1 Garen = Hero()
OK,你的英雄角色已经生成了,让我们看看盖伦现在都有什么(前面四个是属性,后面四个是方法,中间我们先不管它):
1 print dir(Garen) 2 3 C:\Python27\python.exe D:/python/s11/8day/反射/test.py 4 5 ['ATK', 'DEF', 'HP', 'MP', '__doc__', '__init__', '__module__', 'attack', 'running', 'skills', 'walk']
再看看初始属性是多少(恩,HP比MP高,看来是近战英雄):
1 print Garen.__dict__ 2 3 C:\Python27\python.exe D:/python/s11/8day/反射/test.py 4 5 {'HP': 300, 'ATK': 30, 'DEF': 10, 'MP': 100}
既然角色生成了,那接下来当然就该开始操作了。你用鼠标对地图点了一下,一个walk字符串传到了后台程序中,现在让我们看看程序是怎么处理的:
1 if hasattr(Garen,"walk"): #传入walk字符串,使用hasattr函数判断角色是否有该方法 2 walk = getattr(Garen,"walk") #如果有则使用getattr函数取出该方法体,但不执行 3 walk() #最后执行该方法
然后你的角色就会做相应的动作:
1 C:\Python27\python.exe D:/python/s11/8day/反射/test.py 2 3 你的英雄正走向目标
释放技能也一样,当你在键盘按下技能快捷键时把对应技能名称发给程序,程序把名称的技能转换成可执行的动词执行对应的方法。反射说白了就是把你传给对象的字符串参数当成对象里的同名方法去执行,前提是该对象有这个方法。
当然,反射也不仅仅是对象专有,其他扩展到类、模块、函数等容器也都可以使用反射,下面就是反射的四个方法:1 hasattr():根据输入参数判断某个容器中是否有已该参数为名称的内容,如果有则返回true 2 getattr():取出容器中以参数为名称的内容 3 setattr():对容器中以参数为名称的内容进行修改 4 delattr():删除容器中以参数为名称的内容
一般后面两个比较少用,了解即可
- 断言
断言是一个比较有意思的东西,它就想一个条件判断一样,只有满足时才会让程序走下去,不然就报错。可以用来检测系统环境是否满足程序的需要,一般在测试或者调试时使用
1 assert system == "mac" #程序只能在mac上执行,如果不满足则不让执行程序
- 单例模式
既是一个类,只实例化一次,只占用一块内存,各程序想用这个功能的时候就不用再实例化一个对象,而是调用同一个实例,共享内存。
举例:程序会有一个专门连接数据库的类,当用户查询数据库都会实例化一次,创建一个用于连接的对象,如果并发大的话会很浪费内存资源,使用单例实例的话就只需要实例化一次,之后大家共用一个连接实例,这样能节省大量资源。
单例模式类的创建:1 1 class Foo(object): 2 2 __instance = None #_instance设为None,表明该类还没有实例化过 3 3 4 4 @staticmethod #设为静态方法 5 5 def singleton(): 6 6 if Foo.__instance: #判断是否实例化过,如果__instance存在则不再创建实例,而是直接返回第一次创建的实例 7 7 return Foo.__instance 8 8 else: 9 9 Foo.__instance = Foo() #如果还没有实例化过,则将实例化的对象实例绑定到__instance上,并返回该实例 10 10 return Foo.__instance
单例模式对象的创建:
1 obj = Foo.singleton()