'''1、什么是元类
在python中一切皆对象,那么我们用class关键字定义的类本身也是一个对象
负责产生该对象的类称之为元类,即元类可以简称为类的类
class Foo: # Foo=元类() #一切皆对象,类加括号产生对象
pass
2、为何要用元类
元类是负责产生类的,所以我们学习元类或者自定义元类的目的
是为了控制类的产生过程,还可以控制对象的产生过程
3、如何用元类'''
#1、储备知识:内置函数exec的用法
cmd="""x=1
def func(self):
pass"""class_dic={}exec(cmd,{},class_dic) #exec会将cmd字符串中的代码拿出来执行一次,将产生的名字丢掉事先定义好的class_dic空字典中
print(class_dic) #{'x': 1, 'func': }
#2、创建类的方法有两种#大前提:如果说类也是对象的话,那么用class关键字去创建类的过程也是一个实例化的过程#该实例化的目的是为了得到一个类,调用的是元类#2.1 方式一:用的默认的元类type
class People: #People=type(...)--------默认的元类type实例化出一个对象Pelple,实例化的结果也是一个对象
country='China'
def __init__(self,name,age):
self.name=name
self.age=agedefeat(self):print('%s is eating' %self.name)
peo=People('EGON',18)print(peo) #------------<__main__.People object at 0x000001F635282E10>*********调用类实例化出对象
print(type(People)) #------------*****************************************调用元类实例化出类
"""重点"""
#2.1.1 创建类的3个要素:类名,基类,类的名称空间
class_name='People' #类名,是一个字符串,---------由上面的class定义类我们知道,创建类的三要素:类名,基类,类的名称空间
class_bases=(object,) #基类,----------------------我们通过__bases__,知道基类是一个元组的形式
class_dic={} #类的名称空间,---------------通过__dict__,知道类的名称空间的是一个字典
class_body="""country='China'
def __init__(self,name,age):
self.name=name
self.age=age
def eat(self):
print('%s is eating' %self.name)""" #--------将类体代码放到一个字符串中
exec(class_body,{},class_dic)#执行字符传中的代码,将产生的名字方到class_dic的名称空间中,即之前定义类将产生的名字放到类的名称空间中
#准备好创建类的三要素#print(class_name) #-------People#print(class_bases) #-------(,)#print(class_dic) #-------{'country': 'China', '__init__': , 'eat': }
#People=type(类名,基类,类的名称空间) #调用元类就可以产生一个类这个对象
People1=type(class_name,class_bases,class_dic) #将事先定义好的类的三要素放到当做参数传给元类,调用元类即产生对象
print(People1) #--------自定义类产生的结果
obj1=People1('egon',18)print(People) #--------,class定义类产生的结果
obj=People('egon',18)
obj1.eat()
obj.eat()"""----------------------------------------重点----------------------------------------"""
#2.2 方式二:用的自定义的元类
class Mymeta(type): #只有继承了type类才能称之为一个元类,否则就是一个普通的自定义类
def __init__(self,class_name,class_bases,class_dic):print(self) #现在是People
print(class_name)print(class_bases)print(class_dic)
super(Mymeta,self).__init__(class_name,class_bases,class_dic) #重用父类的功能
#分析用class自定义类的运行原理(而非元类的的运行原理):#1、拿到一个字符串格式的类名class_name='People'#2、拿到一个类的基类们class_bases=(obejct,)#3、执行类体代码,拿到一个类的名称空间class_dic={...}------------------前三步就是造类的三要素#4、调用People=type(class_name,class_bases,class_dic)----------------调用元类(类)产生类(对象)------------调用类产生对象
class People(object,metaclass=Mymeta): #People=Mymeta(类名,基类们,类的名称空间)------metaclass=Mymeta是自定义的元类名
country='China'
def __init__(self,name,age):
self.name=name
self.age=agedefeat(self):print('%s is eating' %self.name)"""----------------------------------------重点----------------------------------------"""
"""应用:自定义元类控制类的产生过程,类的产生过程其实就是元类的调用过程------(对象的产生过程就是调用类的过程)"""
class Mymeta(type): #只有继承了type类才能称之为一个元类,否则就是一个普通的自定义类---------------必须要继承type类
def __init__(self,class_name,class_bases,class_dic): #在自定义类之上添加逻辑判断
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('People',(object,),{....})
"""这是People类"""country='China'
def __init__(self,name,age):
self.name=name
self.age=agedefeat(self):print('%s is eating' %self.name)#3 储备知识:__call__
classFoo:def __call__(self, *args, **kwargs):print(self) #<__main__.Foo object at 0x000002193E892E10>
print(args) #(1, 2, 3)----------------*args接收位置参数,存成元组的形式
print(kwargs) #{'x': 1, 'y': 2}---------**kwargs接收关键字参数,存成字典的形式
obj=Foo() #调用类不会自动触发,会在调用对象时自动触发,通过self也可以看出,是调用对象时自动触发
## 要想让obj这个对象变成一个可调用的对象,需要在该对象的类中定义一个方法__call__方法## 该方法会在调用对象时自动触发#obj(1,2,3,x=1,y=2) #调用对象时自动触发__call__方法,并将对象自动传入
"""-----------------------------------------------重点---------------------------------------------"""
#4、自定义元类来控制类的调用的过程,即类的实例化过程
classMymeta(type):def __call__(self, *args, **kwargs): #会在调用对象时自动触发,此时的对象时一个类,即People
#print(self) # self是People
#print(args)
#print(kwargs)
#return 123
"""调用类产生一个对象,发生两件事""" #和class定义类,调用类一样发生两件事
#1、先造出一个People的空对象
obj=self.__new__(self) #造出了一个自定义类People的空对象
#2、为该对空对象初始化独有的属性
#print(args,kwargs)
self.__init__(obj,*args,**kwargs) #对空对象进行初始化,空对象传入,以及参数原封不动的传入
#3、返回一个初始好的对象
return obj #将造出的对象返回,
'''**********************************看成一个对象************************************************'''
class People(object,metaclass=Mymeta): #自定义类People,元类是Mymeta,元类必须继承type类,否则就不是元类
country='China'
def __init__(self,name,age):
self.name=name
self.age=agedefeat(self):print('%s is eating' %self.name)def __new__(cls, *args, **kwargs): #对象自己中有__new__属性,先从对象自己的名称空间中找,自己没有在到自己的类中找
print(cls)#cls.__new__(cls) # 错误 #自己有调用了自己的__new__,这样就出现无线递归,所以会报错
obj=super(People,cls).__new__(cls) #自己中有,我们任然让其取继承父类中的__new__属性,来产生一个空对象,然后将对象初始化,拿到一个返回值
returnobj'''**********************************看成一个对象************************************************'''
"""-----------------------------------------------重点---------------------------------------------"""
#分析:调用Pepole的目的#1、先造出一个People的空对象#2、为该对空对象初始化独有的属性#obj1=People('egon1',age=18)#obj2=People('egon2',age=18)#print(obj1)#print(obj2)
#obj=People('egon',age=18)#print(obj.__dict__)#print(obj.name)#obj.eat()