Python元类
默认type元类的底层工作原理:
#方式一:用class关键字去创建,用的就是默认的元类 class People: #People=type(...) country='China' def __init__(self,name,age): self.name=name self.age=age def eat(self): print('%s is eatting'%self.name) #查看对象obj的类 obj=People('张三',34) print(type(obj)) #查看People的类 print(type(People)) #<class 'type'> 说明People的类是 --------------------------------------------------------------------------- #分析默认元类的底层实现原理 class_name='People' class_bases=(object,) #元祖格式 class_dic={ } class_body=''' country='China' def __init__(self,name,age): self.name=name self.age=age def eat(self): print('%s is eatting'%self.name) ''' 利用exec通过类体代码得到class_dic,让字典里面有值 exec(class_body,{},class_dic) #exec模拟类定义阶段造名称空间的过程 准备好创建类的三要素 print(class_name) print(class_bases) print(class_dic) People=type(类名,基类,类的名称空间) 根据一切皆对象的观念,用type(people)发现People实质是type()一个类实例化得到的结果 验证people是调用type元类实例化得到了这个效果(是用的默认的元类type) 实质:class People():class关键字调用的底层原理 People=type(class_name,class_bases,class_dic) === 等同于class People(): print(People)
自定义元类:初始模板
#方式二:用自定义元类 #People=type(class_name,class_bases,class_dic) 默认的元类 class Mymeta(type): #只有继承了type类才能称之为一个元类,否则就是一个普通的自定义类 # 重点self就是People类,这里其实就是People=Mymeta(...)实例化得到的一个对象 #触发init方法的时候,会将people当做第一个参数出入,即self 就是 People def __init__(self,class_name,class_bases,class_dic): #自己定义重写init print(self) #现在就是People这个类 <class '__main__.People'> print(class_name) #People print(class_bases) #(<class 'object'>,) print(class_dic) #People类体代码执行过程中产生的一堆名字 super(Mymeta,self).__init__(class_name,class_bases,class_dic) # 分析用class自定义类的运行原理(而非元类的的运行原理): # 1、拿到一个字符串格式的类名class_name='People' # 2、拿到一个类的基类们class_bases=(obejct,一堆类A,B等..) 基类默认继承object类 # 3、执行类体代码,拿到一个类的名称空间class_dic={...},类体执行过程中产生的名字 # 4、调用People=type(class_name,class_bases,class_dic) # (继承的父类,metaclass=type默认的元类) class People(object, metaclass=Mymeta): # People=Mymeta(class_name,class_bases,class_dic) country = 'China' def __init__(self,name,age): self.name=name self.age=age def eat(self): print('%s is eatting'%self.name)
应用一:(控制class定义类的过程)
# 自定义类应用一:自定义元类控制类的产生过程,类的产生过程其实就是元类的调用过程(限定类首字母大写,必须要有文档注释) class Mymeta(type): def __init__(self,class_name,class_bases,class_dic): #自己定义重写init if class_dic.get('__doc__') is None or len(class_dic.get('__doc__').strip())==0: #限定类里面必须要有文档注释 raise TypeError('类中必须要有文档注释,并且文档不能为空') if not class_name.istitle(): #限定类名首字母必须大写 raise TypeError('类名首字母必须大写') super(Mymeta,self).__init__(class_name,class_bases,class_dic) #重用父类功能 class People(object, metaclass=Mymeta): # People=Mymeta(class_name,class_bases,class_dic) '''这是People类''' country = 'China' def __init__(self,name,age): self.name=name self.age=age def eat(self): print('%s is eatting'%self.name)
应用二:
__call__ 实现让对象,变成一个可调用的对象,即元类里面如果想实现people()一定是对象people对应的类中有__call__的方法才行,
默认type元类里面有的
#简单说明下我们分析的开始 # 实现目的: obj() # class Foo: # pass # # obj=Foo() # obj() #报错: 'Foo' object is not callable
#改进:在Foo内定义一个call方法 # class Foo: # def __call__(self, *args, **kwargs): # print(self) #<__main__.Foo object at 0x031A1D30> # print(args) # () # print(kwargs) #{} # # obj=Foo() # obj()
__call__方法在元类例如的应用实例1:控制对象(指的就是People对象)的产生过程
#思考People也是对象,想要控制People对象的调用过程,就是自定义类的实例化过程, #即通过控制对象People类Mymeta里面的call方法 class Mymeta(type): def __call__(self, *args, **kwargs): #最终接收到的值只要传给People里面init方法里面用的 # print(self) # self是People # print(args) #('egon',) # print(kwargs) #{'age': 18} # return 123 如果这里不定义,name print(obj)拿到的返回值就是None #1、先造出一个People的空对象 obj=self.__new__(self) #People要找new这个属性,默认用type里面的__call__ #2、为该对空对象初始化独有的属性(为对象初始化功能的属性) self.__init__(obj,*args,**kwargs) #没有自定传值一说,现在是控制底层的创建过程,最终是将值传给People的init用 #3、返回一个初始好的对象 return obj class People(object,metaclass=Mymeta): country='China' def __init__(self,name,age): self.name=name self.age=age # def __new__(cls, *args, **kwargs): #为了验证查找new的顺序(先找自己-》再找类Mymeta-》再找type) # print('People自己的new属性') def __new__(cls, *args, **kwargs): # print(cls) # cls.__new__(cls) #错误的写法,如果自由里面这样写,会变成死循环 obj=super(People,cls).__new__(cls) #可以通过super让其去父类里面找 return obj #将people看成是一个对象,people()能调用说明People对象类中有__cal obj=People('egon',age=18) #‘egon’->args, print(obj.__dict__) #调对象拿到的返回值是None,如果不设置返回值,就是None
注意几点:
第一点:__call__:只有在调用对象时才会触发即People()
其实上面加了元类后,与class直接调用元类没有太多变动,