python 面向对象(高级篇)
异常处理
为了增加程序的友好性,一般在程序报错的时候不会将错误直接返回用户,而是封装好一个提示页面给用户
最简单的结构
try:
# 代码块 逻辑
pass
except Exception as e:
# e是Exception的对象,对象中封装了错误信息
# 上述代码块出错,执行当前块的内容
pass
断言
assert 条件 # 不满足条件直接报错,想使用程序必须报错,强制用户服从,不服从就报错(可以捕获但是一般不捕获
需求:将用户输入的两个数字相加
def add(a,b): return int(a) + int(b) num1 = input('请输入第一个数字>>') num2 = input('请输入第二个数字>>') try: add(num1,num2) except Exception as e: print('发生异常%s'% e)
# try语句格式 try: pass except IndexError as e: print('IndexError',e) except ValueError as e: print('ValueError',e) except Exception as e: print('Exception',e) else: print('try中的代码没出错执行这里') finally: print('最后一定会执行这里') try: raise Exception('心情不好,主动触发异常') except Exception as e: print(e)
也可以通过主动触发异常触发自定义异常,会返回自定义异常类中的__str__方法返回值
常用异常
AttributeError 试图访问一个对象没有的属性,比如foo.x,但是foo没有属性x IOError 输入/输出异常;常见为无法打开文件 ImportError 无法引入模块或包;常见为路径问题或名称错误 IndentationError 语法错误(的子类) ;代码没有正确对齐 IndexError 下标索引超出序列边界,比如当x只有三个元素,却试图访问x[5] KeyError 试图访问字典里不存在的键 KeyboardInterrupt Ctrl+C被按下 NameError 使用一个还未被赋予对象的变量 SyntaxError Python代码非法,代码不能编译(个人认为这是语法错误,写错了) TypeError 传入对象类型与要求的不符合 UnboundLocalError 试图访问一个还未被设置的局部变量,基本上是由于另有一个同名的全局变量,导致你以为正在访问它 ValueError 传入一个调用者不期望的值,即使值的类型是正确的
反射
python中由以下4个函数提供反射功能 : hasattr、getattr、setattr、delattr ; 这四个函数分别对应对象内部的操作为>> 检查是否含有某成员、获取成员、设置成员、删除成员
上述函数接受两个参数,第一个参数为对象或模块,第二个参数是一个字符串
class F006: static_foo = '公有静态字段' __static_foo = '私有静态字段' def __init__(self,name,age): self.name = name self.age = age def show(self): print(self.name) def __show(self): print(self.name) obj = F006('nathaniel',20) # 检查是否含有某成员 ret = hasattr(obj,'static_foo') print(ret) ret = hasattr(obj,'__static_foo') print(ret) ret = hasattr(obj,'__init__') print(ret) ret = hasattr(obj,'show') print(ret) ret = hasattr(obj,'__show') print(ret) # 从对象中获取成员 ret = getattr(obj,'static_foo') print(ret) # ret = getattr(obj,'__static_foo') # print(ret) ret = getattr(obj,'__init__') print(ret) ret = getattr(obj,'show') print(ret) # ret = getattr(obj,'__show') # print(ret) # 设置对象中的成员 setattr(obj,'show',lambda x:print(x)) obj.show('233') setattr(obj,'static_foo',999) print(obj.static_foo) setattr(obj,'new_num',888) print(obj.new_num) # 删除对象中的成员 delattr(obj,'name') print(obj.name) # >> 报错 AttributeError: 'F006' object has no attribute 'name'
python的反射,它的核心本质其实就是利用字符串的形式去对象(模块)中操作(查找/获取/删除/添加)成员,是一种基于字符串的事件驱动!!!
举个栗子 >> 一个web页面,需要根据用户输入的URL不同,调用不同的函数,实现不同的操作,也就是一个简单的路由,这在web框架里是核心部件之一
你可能会这样写
# web框架实例之普通青年版 class Web0: def __init__(self,name): self.name = name def home_page(self): print('%s进入了首页'% self.name) def login_page(self): print('%s进入了登录页'% self.name) def logout_page(self): print('%s进入了退出页'% self.name) def news_page(self): print('%s进入了新闻页'% self.name) obj = Web0('Nathaniel') inp = '' while inp != 'q' and inp != 'Q': inp = input('请输入需要查看的url>>>') if inp == 'home_page': obj.home_page() elif inp == 'login_page': obj.login_page() elif inp == 'logout_page': obj.logout_page() elif inp == 'news_page': obj.news_page() else: if inp != 'q' and inp != 'Q': print('404')
有毛病嘛?没毛病.然而,我们考虑一个问题,如果有成百上千个URL呢(这很正常)?难道我们手动去写成百上千条if语句?显然不现实,呐,反射就来了>>我们对上述代码进行如下优化
# web框架实例之装逼青年版 class Web1: def __init__(self,name): self.name = name def home_page(self): print('%s进入了首页'% self.name) def login_page(self): print('%s进入了登录页'% self.name) def logout_page(self): print('%s进入了退出页'% self.name) def news_page(self): print('%s进入了新闻页'% self.name) obj = Web1('Nathaniel') inp = '' while inp != 'q' and inp != 'Q': inp = input('请输入需要查看的url>>>') if hasattr(obj,inp): getattr(obj,inp)() else: if inp != 'q' and inp != 'Q': print('404')
单例模式
单例模式,是一种常用的软件设计模式.在它的核心结构中只包含一个被称为单例的特殊类.通过单例模式可以保证系统中应用该模式的类一个类只有一个实例,即一个类只有一个对象实例
举个栗子,某服务器程序的配置信息存放在一个文件当中,客户端通过一个AppConfig的类来读取配置文件的信息.如果在程序运行期间,有很多地方都需要使用配置文件的内容,也就是说,很多地方都需要创建 AppConfig 对象的实例,这就导致系统中存在多个 AppConfig 的实例对象,严重浪费内存资源,尤其是在配置文件内容很多的情况下.事实上,类似 AppConfig 这样的类,我们希望在程序运行期间只存在一个实例对象
使用模块实现单例模式
python模块天然就是单例模式,当模块第一次被导入时会生成一个.pyc文件,第二次导入该模块就会直接执行.pyc文件而不会再次执行模块里的代码,因此我们可以把需要的函数和数据都定义在一个模块当中,这样就可以保证每次使用的都是同一个对象了
# 使用模块实现单例模式
class Foo7:
def __init__(self):
pass
def foo0(self):
pass
obj = Foo7()
# 将上面的代码保存在文件 xxx.py
中,要使用时,直接在其他文件中导入此文件中的对象,这个对象即是单例模式的对象
from xxx.py import obj
print(obj)
我们发现无论在哪里导入obj对象,它的内存地址都是一样的
使用装饰器实现单例模式
# 使用装饰器实现单例模式 def make_singleton(cls): _instance = {} def _singleton(*args, **kwargs): if cls not in _instance: _instance[cls] = cls(*args, **kwargs) return _instance[cls] return _singleton @make_singleton class Foo8: pass obj0 = Foo8() obj1 = Foo8() obj2 = Foo8() obj3 = Foo8() print(obj0) print(obj1) print(obj2) print(obj3)
我们发现,无论用Foo8创建多少实例对象,他们所使用的都是同一个内存地址
使用类实现单例模式
# 使用类实现单例模式 class Singleton(object): def __init__(self): pass @classmethod def instance(cls, *args, **kwargs): if not hasattr(Singleton, "_instance"): cls._instance = Singleton(*args, **kwargs) return cls._instance obj0 = Singleton.instance() obj1 = Singleton.instance() obj2 = Singleton.instance() obj3 = Singleton.instance() print(obj0) print(obj1) print(obj2) print(obj3)
这种方式实现的单例模式,使用时会有限制,以后实例化必须通过 obj = Singleton.instance() , 如果用 obj=Singleton() ,这种方式得到的不是单例
使用__new__方法实现(推荐使用,方便)
我们知道,当我们实例化一个对象时,是先执行了类的__new__方法(我们没写时,默认调用object.__new__),实例化对象;然后再执行类的__init__方法,对这个对象进行初始化,所有我们可以基于这个,实现单例模式
# 使用__new__方法实现(推荐使用,方便) class Singleton(object): def __init__(self): pass def __new__(cls, *args, **kwargs): if not hasattr(Singleton, "_instance"): if not hasattr(Singleton, "_instance"): Singleton._instance = object.__new__(cls) return Singleton._instance obj0 = Singleton() obj1 = Singleton() obj2 = Singleton() obj3 = Singleton() print(obj0) print(obj1) print(obj2) print(obj3)
简单粗暴的说,就是一个类无论你实例化多少次,它的对象始终都是一个内存地址