2017年4月24日 python 之反射
什么是反射:
反射的概念是由Smith在1982年首次提出的,主要是指程序可以访问、检测和修改它本身状态或行为的一种能力(自省)。这一概念的提出很快引发了计算机科学领域关于应用反射性的研究。它首先被程序语言的设计领域所采用,并在Lisp和面向对象方面取得了成绩。
python的反射:
由于python中一切事物都是对象,所以都可以使用反射,这种用字符串形式操作对象相关的属性。
基于类的反射:
# 基于类的反射 class People: country="china" def __init__(self,name): self.name=name def walk(self): print("%s walk",self.name) h=People("ada") # 利用布尔值返回类或对象有没有这个属性 print(hasattr(People,"country")) print(hasattr(h,"country")) print(hasattr(h,"walk")) #获取类或对象的某个属性 res1=getattr(People,"country") print(res1) res2=getattr(h,"country") print(res2) res3=getattr(h,"walk") print(res3) #设置类或对象的某个属性并查看 setattr(h,"age",18) print(h.__dict__) #删除类或对象的某个属性并查看 delattr(h,"age") print(h.__dict__)
基于模块的反射:
# 基于模块的发射 # 反射当前模块的属性 import sys x=1111 class Foo: pass def s1(): print('s1') def s2(): print('s2') this_module=sys.modules[__name__] print(this_module) #这句话所做的操作:遍历s1在不在this_module 的字典__dict__中 print(hasattr(this_module,'s1')) print(getattr(this_module,'s2')) print(this_module.s1) print(this_module.s2) # import importlib # t=importlib.import_module("time") # print(t.time()) # 字符串导入模块,官方不推荐 # m=input("please input your module:") # m1=__import__(m) # print(m1) # print(m1.time())
对类的属性的操作:__getattr__, __setattr__, __delattr__
class Foo: def __init__(self,x): self.name=x #属性不存在的情况才会触发 def __getattr__(self, item): print("%s %s--->"%(item,type(item))) #增加/修改等赋值操作触发他的运行 def __setattr__(self, key, value): if not isinstance(value,str): raise TypeError("must be str") self.__dict__[key]=value #删除操作触发他的运行 def __delattr__(self, item): self.__dict__.pop() f=Foo("egon") f.eat #Foo没有eat这个属性所以,所以触发__getattr__,打印:eat <class 'str'>---> f.age=18 #报错,因为不符合设置属性时候,我们判断的是必须是字符串,因此抛出:must be str
重新定制我们的list列表类型:使他添加,插入都必须是int.
class List(list): def append(self,object:str): if not isinstance(object,int): raise TypeError("must int") super().append(object) def insert(self, index: int, object): if not isinstance(object,int): raise TypeError("must int") super().insert(index,object) l=List([1,2,3]) print(l) l.append(4) print(l) l.insert(0,-1) # l.insert(0,"hahahaha") #报错,因为我们重写了增加和插入方法,非int类型插入报错,必须是int类型 print(l)
反射的好处:
(1)即插即用,类似优盘,你可以事先把主要的逻辑写好(只定义接口),然后后期再去实现接口的功能,后期绑定。
#同一文件目录下,建立FtpClient.py,只是粗略定一个客户端类,没有具体实现,服务端可以判断一下然后继续执行,客户端后面写也行 class FtpClient: 'ftp客户端,现在并没有实现具体的功能' def __init__(self,addr): print('正在连接服务器[%s]' %addr) self.addr=addr
# 同一文件下建立server.py 然后引入客户端包,运行,假如没有反射机制,getattr()方法就无法判断客户端有没有get 方法 import FtpClient #忽律解释器的红线 f1=FtpClient.FtpClient('192.168.1.1') if hasattr(f1,'get'): func_get=getattr(f1,'get') func_get() else: print('---->目前不存在此方法') print('先处理其他的逻辑')
(2)动态导入模块(基于反射当前模块成员)
# import importlib # t=importlib.import_module("time") # print(t.time()) #导入当前路径下包impag下的hello.py # x=importlib.import_module("impag.hello") # 字符串导入模块,官方不推荐 # m=input("please input your module:") # m1=__import__(m) # print(m1) # print(m1.time())
二次加工标准类型:包装和授权
#包装
class List(list): #继承list所有的属性,也可以派生出自己新的,比如append和mid def append(self, p_object): ' 派生自己的append:加上类型检查' if not isinstance(p_object,int): raise TypeError('must be int') super().append(p_object) @property def mid(self): '新增自己的属性' index=len(self)//2 return self[index] l=List([1,2,3,4]) print(l) l.append(5) print(l) # l.append('1111111') #报错,必须为int类型 print(l.mid) #其余的方法都继承list的 l.insert(0,-123) print(l) l.clear() print(l)
# 授权是包装的一个特性。 # 包装的过程:一个类型通常是对已存在的类型的一些定制,这种做法可以新建,修改或删除原有产品的功能,其它的则保持原样。 # 授权的过程:所有更新的功能都是由新类的某部分来处理,但已存在的功能就授权给对象的默认属性。 import time class FileHandle: def __init__(self,filename,mode='r',encoding='utf-8'): self.file=open(filename,mode,encoding=encoding) def write(self,line): t=time.strftime('%Y-%m-%d %T') self.file.write('%s %s' %(t,line)) def __getattr__(self, item): return getattr(self.file,item) f1=FileHandle('b.txt','w+') f1.write('时间和文本组成记录') f1.seek(0) print(f1.read()) f1.close()