面向对象高级反射,内置方法,元类

文章来源:一氧化碳中毒   发布时间:2021-8-19 13:54:01   点击数:
  中科医院专家微信 http://pf.39.net/bdfyy/bdflx/150217/4580351.html
反射Reflection

Python是一门动态语言(Python在定义变量的时候,并没有指定数据类型),反射机制被视为动态语言的关键.

a=HaHab=c=3.14d={a:,b:}什么是反射?

指的是在程序运行过程中可以动态获取对象的信息.当我在调用上述变量的时候,程序需要自动的识别变量的类型:

res1=a*d#通过反射机制,发现a是字符串,d是字典,无法相乘res2=b*c#通过反射机制,程序能正常执行为何要用反射?

deffunc(obj):obj.xfunc(10)

给func传10,10调用x方法.程序报错,10没有x方法.那么如何才能知道可以传什么才不报错呢?加一个判断:

deffunc(obj):ifxnotinobj.__dict__:returnobj.xfunc()

在调用属性之前,先判断一下是否在__dict__中,但是并不是所有的都有__dict__方法的,在python有一种更好的方法判断,这就是反射.

如何实现反射

classPeople:def__init__(self,name,age):self.name=nameself.age=agedefsay(self):print(%s---%s%(self.name,self.age))obj=People(李建钢,18)dir查看对象下有哪些属性

如何获取到obj下面有哪些属性和方法呢?python提供了dir函数实现了该功能.

print(dir(obj))

此列表下面的全是字符串,是不能直接调用的.

dir(obj)[-2]#==nameobj.dir(obj)[-2]#obj只能点出name,不能点出name通过字符串发射到真正的属性上,得到属性值

通过__dict__可以访问到属性对应的值.

print(obj.__dict__[name])

和dir结合起来

print(obj.__dict__[dir(obj)[-2]])四个内置函数的使用

但是并不是所有的都有__dict__方法.所以python内置了四个函数提供使用.

hasattr()判断

print(hasattr(obj,name))#返回布尔值getattr()获取

print(getattr(obj,name))#返回name的值:李建钢setattr()赋值

print(setattr(obj,name,王大锤))#没有返回值delattr()删除

print(delattr(obj,name))

全部通过字符串来操作.

案例

classFtp:defput(self):print(上传功能)defget(self):print(下载功能)definteractive(self):method=input(":").strip()ifhasattr(self,method):getattr(self,method)else:print(指令不存在)obj=Ftp()obj.interactive()内置方法什么是内置方法?

在类内部,以__开头并__结尾的方法.会在某种情况下自动触发.

如何使用内置方法

__str__方法

classPeople:def__init__(self,name,age):self.name=nameself.age=agedefsay(self):print(%s---%s%(self.name,self.age))obj=People(李建钢,18)obj1=int(10)print(obj)print(obj1)

自定义类打印的是对象的函数地址,而内置类打印的是10.说明内置类做了一系列的操作.执行print会触发操作,返回10.

classStr:def__init__(self,name,age):self.name=nameself.age=agedef__str__(self):print(打印实例自动触发该功能)obj=Str(wezhon,18)print(obj)

可以看到print(obj)催发了__str__的运行.但是报错了,提示放回了一个非字符串的值,因为__str__没有指定return,所以返回的是None.

__del__方法在清理对象时触发,会先执行该方法:

元类

在linux中,一切皆为文件;在Python中,一切皆为对象.

调用类实例化,可以得到一个对象.既然说一切皆为对象,那么class也是对象.生成自定义类的类叫做元类(type).

元类就是实例化产生类的类.元类---(实例化)---类---(实例化)---对象.

使用type()函数可以查看被谁实例化出来的.

可以看到元类为type.

class关键字实例化类的步骤类名

class_name="People"#等同于class关键字后接的名字类的基类

class_bases=(object)#等同于类的名字后面()的内容执行类体代码拿到类的名称空间

#类体代码class_dic={}class_body="""def__init__(self,name,age):self.name=nameself.age=agedefsay(self):print(%s-%s%(self.name,self.age))"""#使用exec函数,传入三个参数#1.class_body,传入代码#2.{}全局名称空间#3.将执行后代码所产生的名称空间传入class_dicexec(class_body,{},class_dic)#得到类的名称空间class_dicprint(class_dic)调用元类

#调用type函数生成一个类,赋值给People#传入三个参数,类名,类体代码,名称空间People=type(class_name,class_bases,class_dic)把类赋值People,People只是一个变量名.得到类People,

可以看出类也是一个对象.

如何自定义元类来控制类的产生

在上面的第四步,调用type产生一个类.type是一个元类,能不能自己定义一个元类来产生类呢,这样就可以控制产生类的属性.

定义一个元类

classMymeta(type):pass

只有继承了type的类才是元类.

生成一个类,默认的元类为typemetaclass=type,修改为自己定义的元类Mymeta

classPeople(metaclass=Mymeta):def__init__(self,name,age):self.name=nameself.age=agedefsay(self):print(%s:%s%(self.name,self.age))#默认调用的是type#People=type(class_name,class_bases,class_dic)#现在变为调用Mymeta#People=Mymeta(class_name,class_bases,class_dic)#调用Mymeta发生三件事#1.造一个空对象==People#2.调用Mymeta这个类的__init__方法,完成初始化对象的操作#3.返回初始化的对象

现在People这个类就是通过元类Mymeta产生的了,进而通过Mymeta来控制类的产生.

控制元类

classMymeta(type):def__init__(self):#__init__调用时执行print(runing....)

在生成类的时候提示,Myteta的__init__需要一个参数,但是被传入了四个.因为

People=Mymeta(class_name,class_bases,class_dic)

在产生类的时候传入的本就就有三个参数,在加上自身就是四个,所以在__init__还需要三个形参.

可以看到,打印出来的x,y,z分别为:

变量名基类(默认object)名称空间

现在,我在元类里加上一个判断,如果产生的类首字母不是大写,将会报错.

继续,添加控制语句,如果产生的类没有说明文档将会报错.如果有说明文档将打印说明文档.__new__方法

ok,回到调用Mymeta这里,在调用Mymeta会发生三件事:

造一个空对象==People调用Mymeta这个类的__init__方法,完成初始化对象的操作返回初始化的对象

在第一步的时候会通过__new__方法产生空对象.并且在__init__方法之前执行.

classMymeta(type):def__new__(cls,*args,**kwargs):pass

分别打印cls,*args,**kwargs.

cls:元类*args:类名,基类,类的名称空间**kwargs:空字典

最后需要返回结果,不然不会触发__init__.

classMymeta(type):def__new__(cls,*args,**kwargs):print(in__new__)#returnsuper().__new__(cls,*args,**kwargs)returntype.__new__(cls,*args,**kwargs)wezhon

转载请注明:http://www.lwblm.com/bytj/12319.html