一.什么是元类
在python中一切皆对象,那么自定义的类也是一个对象,用来实例化产生该类的类称为元类\
二.为何用元类
元类是负责产生类的,那么我们自定义元类的目的就是为了控制类的产生过程,还可以控制对象的产生过程.
三.如何用元类
创建类的方法有两种
1.使用默认的元类type(type是所有自定义元类的类,如果没有继承type,那么它就是一个普通的自定义类,只有继承了type的类才是元类)
因为一切皆对象,那么用class自定义类时,就是在调用type元类实例化的一个过程
class People:#People=type(...)
def __init__(self,name,age):
self.name=name
self.age=age
那么问题来了,是怎么创建类的呢?
创建类的三要素,类名,基类们,类的名称空间
类名:顾名思义,就是一个名字,假设class_name = 'People'
基类:在python3中如果没有明显继承关系的,默认继承object类,所以class_bases = (object,...其他父类们)
类的名称空间:类的名称空间是怎么产生的?是定义类时执行类体内的代码,将产生的名字存进类的名称空间.类的名称空间可以看成是一个字典形式的存在,而代码就是一堆字符.可以定义为class_dic = {}补充知识:我们可以通过exec方法模拟执行python代码的过程
class_bady='''
def __init__(self,name,age):
self.name=name
self.age=age
def eat(self):
print('%s is eating'%self.name)
'''
class_dic={}
exec(cmd,{},class_dic)
print(class_dic)
这样我们拿到了三要素了,可以通过tpye来创建了.
People = type(class_name,class_bases,class_dic)
分析用class来定义类的运行原理(而非元类)
1.首先拿到一个字符串格式的名字class_name = 'People'
2.再拿到一个类的基类们 class_bases = (object,)
3.执行类体内的代码,产生一个类的名称空间class_dic = {...}
4.调用People = type(class_name,class_bases,class_dic)
2.自定义元类及目的
class Mymeta(type):#只有继承了type的类才是一个元类,否则只是一个普通的自定义类
def __init__(self,class_name,class_bases,class_dic):
print(self)
print(class_name)
print(class_bases)
print(class_dic)
super().__init__(class_name,class_bases,class_dic)
class People(object,metaclass=Mymeta):#Peolpe=Mymeta('People',(object,),{...})
#metaclass就是指定一个类为元类
def __init__(self,name,age,sex):
self.name=name
self.age=age
self.sex =sex
obj = People('tom',18,'male')
2.1.自定义元类控制类的创建
class Mymeta(type):
def __init__(self,class_name,class_bases,class_dic):
super().__init__(class_name, class_bases, class_dic)#重用父类的功能
dic = class_dic.get('__doc__')
if dic is None or len(dic) == 0 or len(dic.strip('\n ')) == 0:
raise TypeError('必须要有文档注释')
class People(object,metaclass=Mymeta):
'''People文档注释内容'''
def __init__(self,name,age,sex):
self.name=name
self.age=age
self.sex=sex
print(People.__dict__)
2.2.自定义元类来控制类的调用
class A:
def __call__(self, *args, **kwargs):
print(self)
print(args)
print(kwargs)
obj = A()
obj(1,2,3,x=34)
#要让obj对象变成一个可调用的对象,必须再它所在的类里面定义一个__call__方法,该方法会在调用对象时自动触发
#调用obj的返回值就是__call__的返回值
通过上面的列子我们看到,调用对象就是调用对象所在类下的__call__方法.那么我们把People看成一个对象,那么调用People也就是自动触发了它所在类下必然存在的的__call__方法
class Mymeta(type):#只有继承了type的类才是一个元类,否则只是一个普通的自定义类
def __call__(cls, *args, **kwargs):
print(cls)
print(args)
print(kwargs)
return 1
class People(object,metaclass=Mymeta):
def __init__(self,name,age,sex):
self.name=name
self.age=age
self.sex =sex
obj = People('tom',18,'male')
print(obj)
默认的,在调用obj = People('tom',18,'male')是会发生三件事
1.创建一个空对象obj
2.执行__init__方法为空对象初始化一些属性('tom',18,'male')
3.返回这个对象obj
对应的People的类中的__call__方法也应该做这三件事
class Mymeta(type): # 只有继承了type的类才是一个元类,否则只是一个普通的自定义类
def __call__(self, *args, **kwargs):#self = People
#1.创建一个空对象
obj = self.__new__(self)#obj是People这个类的对象
#这里的__new__方法是调用了object中的__new__方法,看下面的属性查找关系
#2.用__init__方法为对象初始化属性
self.__init__(obj,*args,**kwargs)#调用People下的__init__方法将接受到的参数原封不动的进行传参
#在初始化后,obj.__dict__就有了
# obj.__dict__ = {('_%s__%s'%(self.__name__,k)):v for k,v in obj.__dict__.items()}
# 基于这个逻辑我们可以施加我们想要的控制过程,比如将对象的属性私有化
#3.返回初始化好的对象obj
return obj
class People(object, metaclass=Mymeta):
def __init__(self, name, age, sex):
self.name = name
self.age = age
self.sex = sex
obj = People('tom', 18, 'male')
print(obj.__dict__)
3.属性查找
所有的属性查找其实都是先找对象再找对象所在的类
如果把类也当作对象看的话,属性查找分为两层.
第一层为对象层,先查对象(自定义的类),再查对象所在的父类,(基于c3算法MRO列表查找),一直找到object
第二层是类层(此时把类当作对象来看),如果对象层没有找到的话再查找元类层