一、什么是反射
反射的概念是由Smith在1982年首次提出的,主要是指程序可以访问、检测和修改它本身状态或行为的一种能力(自省)。这一概念的提出很快引发了计算机科学领域关于应用反射性的研究。它首先被程序语言的设计领域所采用,并在Lisp和面向对象方面取得了成绩。
二、四个可以实现自省的函数(下列方法适用于类和对象)
1、hasattr(object,name)
判断object中有没有一个name字符串对应的方法或属性
2、getattr(object, name, default=None)
获取属性
3、setattr(x, y, v)
设置属性
4、delattr(x, y)
删除属性
示例:
1 class BlackMedium: 2 feature='Ugly' 3 def __init__(self,name,addr): 4 self.name=name 5 self.addr=addr 6 7 def sell_house(self): 8 print('%s 黑中介卖房子啦,傻逼才买呢,但是谁能证明自己不傻逼' %self.name) 9 def rent_house(self): 10 print('%s 黑中介租房子啦,傻逼才租呢' %self.name) 11 12 b1=BlackMedium('万成置地','回龙观天露园') 13 14 #检测是否含有某属性 15 print(hasattr(b1,'name')) 16 print(hasattr(b1,'sell_house')) 17 18 #获取属性 19 n=getattr(b1,'name') 20 print(n) 21 func=getattr(b1,'rent_house') 22 func() 23 24 # getattr(b1,'aaaaaaaa') #报错 25 print(getattr(b1,'aaaaaaaa','不存在啊')) 26 27 #设置属性 28 setattr(b1,'sb',True) 29 setattr(b1,'show_name',lambda self:self.name+'sb') 30 print(b1.__dict__) 31 print(b1.show_name(b1)) 32 33 #删除属性 34 delattr(b1,'addr') 35 delattr(b1,'show_name') 36 #delattr(b1,'show_name111')#不存在,则报错 37 38 print(b1.__dict__)
执行结果:
1 True 2 True 3 万成置地 4 万成置地 黑中介房子啦,傻逼才租呢 5 不存在啊 6 {'name': '万成置地', 'show_name': <function <lambda> at 0x01142F60>, 'sb': True, 'addr': '回龙观天露园'} 7 万成置地sb
总结:
hasattr(obj,'属性') #obj.属性 是否存在
getattr(obj,'属性') #获取obj.属性 不存在则报错
getattr(obj,'属性','默认值') #获取obj.属性 不存在不会报错,返回那个默认值
setattr(obj,'属性','属性的值') #obj.属性=属性的值
delattr(obj,'属性') #del obj.属性
三、为什么用反射
有俩程序员,一个alex,一个是egon,alex在写程序的时候需要用到egon所写的类,但是egon去跟女朋友度蜜月去了,还没有完成他写的类,alex想到了反射,使用了反射机制alex可以继续完成自己的代码,等egon度蜜月回来后再继续完成类的定义并且去实现alex想要的功能。
总之反射的好处就是,可以事先定义好接口,接口只有在被完成后才会真正执行,这实现了即插即用,这其实是一种‘后期绑定’,什么意思?即你可以事先把主要的逻辑写好(只定义接口),然后后期再去实现接口的功能
示例:
1 class FtpClient: 2 'ftp客户端,但是还么有实现具体的功能' 3 def __init__(self,addr): 4 print('正在连接服务器[%s]' %addr) 5 self.addr=addr
1 #from module import FtpClient 2 f1=FtpClient('192.168.1.1') 3 if hasattr(f1,'get'): 4 func_get=getattr(f1,'get') 5 func_get() 6 else: 7 print('---->不存在此方法') 8 print('处理其他的逻辑') 9 10 不影响alex的代码编写
四、动态导入模块
1、新建一个t.py的文件
1 print('---------------->') 2 def test1(): 3 print('test1') 4 5 def _test2(): 6 print('test2')
2、再创建:m1文件夹,再在他下面创建一个t.py
1 module_t=__import__('m1.t') 2 print(module_t) 3 module_t.t.test1() 4 # from m1.t import * 5 # from m1.t import test,_test2 6 7 import importlib 8 m=importlib.import_module('m1.t') 9 print(m) 10 m.test1() 11 m._test2()
执行结果:
1 ----------------> 2 <module 'm1' (namespace)> 3 test1 4 <module 'm1.t' from 'D:\\python\\day26\\m1\\t.py'> 5 test1 6 test2
五、三个参数,给对象添加属性
__setattr__ 添加/修改属性会触发它的执行
__delattr__ 删除属性的时候会触发
__getattr__ 只有在使用点调用属性且属性不存在的时候才会触发
作用:系统内置函数属性(你定义了就用你定义的函数属性,不定义就用系统默认的函数属性)
综合应用示例:
1 class Foo: 2 x=1 3 def __init__(self,y): 4 self.y=y 5 6 def __getattr__(self, item): 7 print('----> from getattr:你找的属性不存在') 8 9 10 def __setattr__(self, key, value): 11 print('----> from setattr') 12 # self.key=value #这就无限递归了,你好好想想 13 # self.__dict__[key]=value #应该使用它 14 15 def __delattr__(self, item): 16 print('----> from delattr') 17 # del self.item #无限递归了 18 self.__dict__.pop(item) 19 20 #__setattr__添加/修改属性会触发它的执行 21 f1=Foo(10) 22 print(f1.__dict__) # 因为你重写了__setattr__,凡是赋值操作都会触发它的运行,你啥都没写,就是根本没赋值,除非你直接操作属性字典,否则永远无法赋值 23 f1.z=3 24 print(f1.__dict__) 25 26 #__delattr__删除属性的时候会触发 27 f1.__dict__['a']=3#我们可以直接修改属性字典,来完成添加/修改属性的操作 28 del f1.a 29 print(f1.__dict__) 30 31 #__getattr__只有在使用点调用属性且属性不存在的时候才会触发 32 f1.xxxxxx
分开应用:
示例1:__getattr__(重点,因为常用)
1 class Foo: 2 x=1 3 def __init__(self,y): 4 self.y=y 5 6 def __getattr__(self, item): 7 print('执行__getattr__') 8 9 f1=Foo(10) 10 print(f1.y) 11 12 #没有的时候就会触发: __getattr__ 13 print(getattr(f1,'y')) #len(str)---->str.__len__() 14 f1.ssssssssssssssssssssssssssssss
执行结果:
1 10 2 10 3 执行__getattr__
示例2:__delattr__(不常用)
1 class Foo: 2 x=1 3 def __init__(self,y): 4 self.y=y 5 6 def __delattr__(self, item): 7 print('删除操作__delattr__') 8 9 f1=Foo(10) 10 del f1.y 11 del f1.x
执行结果:
1 删除操作__delattr__ 2 删除操作__delattr__
示例3: __setattr__(不常用)
1 class Foo: 2 x=1 3 def __init__(self,y): 4 self.y=y 5 6 def __setattr__(self,key,value): 7 print('__setattr__执行') 8 #self.key=value 9 self.__dict__[key]=value 10 11 #增加键、值到字典中 12 f1=Foo(10) 13 print(f1.__dict__) 14 15 f1.z=2 16 print(f1.__dict__)
执行结果:
1 __setattr__执行 2 {'y': 10} 3 __setattr__执行 4 {'z': 2, 'y': 10}
总结:
obj点的方式去操作属性时触发的方法
__getattr__:obj.属性 不存在时触发
__setattr__:obj.属性=属性的值 时触发
__delattr__:del obj.属性 时触发
六、二次加工标准类型(包装)
包装:python为大家提供了标准数据类型,以及丰富的内置方法,其实在很多场景下我们都需要基于标准数据类型来定制我们自己的数据类型,新增/改写方法,这就用到了我们刚学的继承/派生知识(其他的标准类型均可以通过下面的方式进行二次加工)。
1 class List(list): 2 def append(self, p_object): 3 '我改写的方法' 4 if not isinstance(p_object,str): 5 print('只有字符串类型能被添加到列表中') 6 return 7 # self.append(p_object) #进入无限递归 8 super().append(p_object) 9 def show_mid(self): 10 '我新增的方法' 11 index=int(len(self)/2) 12 print(self[index]) 13 14 15 l1=List('hello') 16 17 l1.append('abc') 18 print(l1,type(l1)) 19 20 #数字无法添加成功 21 l1.append(1) 22 print('-->',l1) 23 24 #基于标准类型新增了功能 25 l1.show_mid()
授权:授权是包装的一个特性, 包装一个类型通常是对已存在的类型的一些定制,这种做法可以新建,修改或删除原有产品的功能。其它的则保持原样。授权的过程,即是所有更新的功能都是由新类的某部分来处理,但已存在的功能就授权给对象的默认属性。
实现授权的关键点就是覆盖__getattr__方法
1、通过触发__getattr__方法,找到read方法
示例1:
1 import time 2 class FileHandle: 3 def __init__(self,filename,mode='r',encoding='utf-8'): 4 self.file=open(filename,mode,encoding=encoding) 5 self.mode=mode 6 self.encoding=encoding 7 8 def __getattr__(self, item): 9 print(item,type(item)) 10 self.file.read #self.file里面有read方法 11 return getattr(self.file,item) #能过字符串来找到,并通过return返回,就找到了read方法, 12 13 f1=FileHandle('a.txt','r') 14 print(f1.file) 15 print(f1.__dict__) #类的字典里,没有read方法,就触发了__getattr__方法 16 print(f1.read) #找到了read方法 17 18 sys_f=open('b.txt','w+') 19 print('---->',getattr(sys_f,'read')) #找到了read方法
执行结果:
1 <_io.TextIOWrapper name='a.txt' mode='r' encoding='utf-8'> 2 3 read <class 'str'> 4 5 <built-in method read of _io.TextIOWrapper object at 0x01638EB0> 6 7 {'encoding': 'utf-8', 'file': <_io.TextIOWrapper name='a.txt' mode='r' encoding='utf-8'>, 'mode': 'r'} 8 9 ----> <built-in method read of _io.TextIOWrapper object at 0x01638F30>
2、往文件里面写入内容
示例2:
1 import time 2 class FileHandle: 3 def __init__(self,filename,mode='r',encoding='utf-8'): 4 # self.filename=filename 5 self.file=open(filename,mode,encoding=encoding) 6 self.mode=mode 7 self.encoding=encoding 8 def write(self,line): 9 print('------------>',line) 10 t=time.strftime('%Y-%m-%d %X') 11 self.file.write('%s %s' %(t,line)) 12 13 def __getattr__(self, item): 14 # print(item,type(item)) 15 # self.file.read 16 return getattr(self.file,item) 17 18 f1=FileHandle('a.txt','w+') 19 f1.write('1111111111111111\n') 20 f1.write('cpu负载过高\n') 21 f1.write('内存剩余不足\n') 22 f1.write('硬盘剩余不足\n')
执行结果:
会创建一个a.txt的文件,并往里面写入内容:
1 2016-12-23 18:34:16 1111111111111111 2 2016-12-23 18:34:16 cpu负载过高 3 2016-12-23 18:34:16 内存剩余不足 4 2016-12-23 18:34:16 硬盘剩余不足
七、isinstance(obj,cls)和issubclass(sub,super)
1、isinstance(obj,cls) 检查是否obj是否是类cls的对象
示例:
1 class Foo(object): 2 pass 3 4 obj = Foo() 5 6 print(isinstance(obj,Foo))
执行结果:
1 True
2、issubclass(sub,super)检查sub类是否是supper类的派生类
示例:
1 class Foo(object): 2 pass 3 4 class Bar(Foo): 5 pass 6 7 print(issubclass(Bar,Foo))
执行结果:
1 True
八、__getattribute__
示例1:
不存在的属性访问,就会触发__getattr__方法
1 class Foo: 2 def __init__(self,x): 3 self.x=x 4 5 def __getattr__(self, item): 6 print('执行的是我') 7 #return self.__idct__[item] 8 9 f1=Foo(10) 10 print(f1.x) 11 f1.xxxxxx #不存在的属性访问,触发__getattr__
执行结果:
1 10 2 执行的是我
示例2:
不管是否存在,都会执行__getattribute__方法
1 class Foo: 2 def __init__(self,x): 3 self.x=x 4 5 def __getattribute__(self, item): 6 print('不管是否存在,我都会执行') 7 8 f1=Foo(10) 9 f1.x 10 f1.xxxxxxx
执行结果:
1 不管是否存在,我都会执行 2 不管是否存在,我都会执行
示例:3:
1 class Foo: 2 def __init__(self,x): 3 self.x=x 4 5 def __getattr__(self, item): #相当于监听大哥的异常,大哥抛出导常,他就会接收。 6 print('执行的是我') 7 # return self.__dict__[item] 8 9 def __getattribute__(self, item): 10 print('不管是否存在,我都会执行') 11 raise AttributeError('抛出异常了') 12 13 f1=Foo(10) 14 f1.x #结果是:10 ,调用会触发系统的 15 f1.xxxxxxx #如果不存在会触发自己定义的
执行结果:
1 不管是否存在,我都会执行 2 执行的是我 3 不管是否存在,我都会执行 4 执行的是我
九、__setitem__,__getitem,__delitem__ (操作字典就用item的方式)
obj[‘属性’]的方式去操作属性时触发的方法
__getitem__:obj['属性'] 时触发
__setitem__:obj['属性']=属性的值 时触发
__delitem__:del obj['属性'] 时触发
示例1:
1 class Foo: 2 3 def __getitem__(self, item): 4 print('getitem',item) 5 return self.__dict__[item] 6 7 def __setitem__(self, key, value): 8 print('setitem') 9 self.__dict__[key]=value 10 11 def __delitem__(self, key): 12 print('delitem') 13 self.__dict__.pop(key) 14 15 f1=Foo() 16 print(f1.__dict__) 17 f1['name']='agon' 18 f1['age']=18 19 print('====>',f1.__dict__) 20 21 del f1['name'] 22 print(f1.__dict__) 23 24 print(f1,['age'])
执行结果:
1 {} 2 3 setitem 4 5 setitem 6 7 ====> {'age': 18, 'name': 'agon'} 8 9 delitem 10 11 {'age': 18} 12 13 <__main__.Foo object at 0x01DC4150> ['age']
示例2:
1 class Foo: 2 def __init__(self,name): 3 self.name=name 4 5 def __getitem__(self, item): 6 print(self.__dict__[item]) 7 8 def __setitem__(self, key, value): 9 self.__dict__[key]=value 10 def __delitem__(self, key): 11 print('del obj[key]时,我执行') 12 self.__dict__.pop(key) 13 def __delattr__(self, item): 14 print('del obj.key时,我执行') 15 self.__dict__.pop(item) 16 17 f1=Foo('sb') 18 f1['age']=18 19 f1['age1']=19 20 del f1.age1 21 del f1['age'] 22 f1['name']='alex' 23 print(f1.__dict__)
执行结果:
1 del obj.key时,我执行 2 3 del obj[key]时,我执行 4 5 {'name': 'alex'}
十、__str__, __repr__,__format__
1、改变对象的字符串显示__str__,__repr__ (只能是字符串的值,不能是非字符串的值)
示例1:
1 l = list('hello') 2 print(1) 3 4 file=open('test.txt','w') 5 print(file)
执行结果:
1 1 2 <_io.TextIOWrapper name='test.txt' mode='w' encoding='cp936'>