1、什么是元类(type)
# 在python中一切皆对象,通过类实例化可以得到对象,那么类是怎么产生的了? 类也是对象,它是通过调用元类(type)实现的。
其关系如下:
元类(type)-------> 实例化---------> Beast类---------->实例化---------->obj(对象)
2、class关键字创建类的逐层分析
以Beast类为例
class Beast(object): def __init__(self, name, age): self.name = name self.age = age self.score = {} def hello(self): print(f'hello,{self.name}')
上文我们基于python中一切皆为对象的概念分析出:我们用class关键字定义的类本身也是一个对象,负责产生该对象的类称之为元类(元类可以简称为类的类),内置的元类为type
class关键字在帮我们创建类时,必然帮我们调用了元类Beast=type(...),那调用type时传入的参数是什么呢?必然是类的关键组成部分,一个类有三大组成部分,分别是:
(1)类名 class_name = 'Beast'
(2)类的基类们class_bases = (object,)
(3)类的名称空间class_dic ,类的名称空间是执行类体代码得到的
class_dic = {} class_body = """ def __init__(self, name, age): self.name = name self.age = age self.score = {} def hello(self): print(f'hello,{self.name}') """ exec(class_body,{},class_dic) 参数一:包含一系列python代码的字符串 参数二:全局作用域(字典形式),如果不指定,默认为globals() 参数三:局部作用域(字典形式),如果不指定,默认为locals() 可以把exec命令的执行当成是一个函数的执行,会将执行期间产生的名字存放于局部名称空间中
(4)调用元类得到Beast类
Beast = type(class_name,class_bases,class_dic)
3、如何自定义元类来控制类的产生
class Mymeta(type): # 只有继承了type类的类才是元类 # 空对象,"People",(object,),{...} def __init__(self,x,y,z): print('run22222222.。。。') print(self.__bases__) # print(x) # print(y) # print(z) # if not x.istitle(): # raise NameError('类名的首字母必须大写啊!!!') # if not self.__doc__: # raise TypeError('必须要有注释') def __new__(cls, *args, **kwargs): # 造Mymeta的对象 print('run1111111111.....') # print(cls,args,kwargs) # return super().__new__(cls,*args, **kwargs) return type.__new__(cls, *args, **kwargs) # People=Mymeta("People",(object,),{...}) # 调用Mymeta发生三件事 # 1、先造一个空对象=>People # 2、调用Mymeta这个类内的__init__方法,完成初始化对象的操作 # 3、返回初始化好的对象 class People(metaclass=Mymeta): '''注释''' def __init__(self,name,age): self.name=name self.age=age def say(self): print('%s:%s' %(self.name,self.age))
强调:
强调: 只要是调用类,那么会依次调用 1、类内的__new__ 2、类内的__init__
4、__call___
class Foo: def __init__(self,x,y): self.x=x self.y=y # obj,1,2,3,a=4,b=5,c=6 def __call__(self,*args,**kwargs): print('===>',args,kwargs) return 123 obj=Foo(111,222) # print(obj) # obj.__str__ res=obj(1,2,3,a=4,b=5,c=6) # res=obj.__call__() print(res)
应用:如果想让一个对象可以加括号调用,需要在该对象的类中添加一个方法__call__
对象()->类内的__call__ 类()->自定义元类内的__call__ 自定义元类()->内置元类__call__
5、自定义元类控制类的调用=》类的对象的产生
class Mymeta(type): # 只有继承了type类的类才是元类 def __call__(self, *args, **kwargs): # 1、Mymeta.__call__函数内会先调用People内的__new__ people_obj=self.__new__(self) # 2、Mymeta.__call__函数内会调用People内的__init__ self.__init__(people_obj,*args, **kwargs) # print('people对象的属性:',people_obj.__dict__) people_obj.__dict__['xxxxx']=11111 # 3、Mymeta.__call__函数内会返回一个初始化好的对象 return people_obj
分析:
类的产生 People=Mymeta()=》type.__call__=>干了3件事 1、type.__call__函数内会先调用Mymeta内的__new__ 2、type.__call__函数内会调用Mymeta内的__init__ 3、type.__call__函数内会返回一个初始化好的对象
class People(metaclass=Mymeta): def __init__(self,name,age): self.name=name self.age=age def say(self): print('%s:%s' %(self.name,self.name)) def __new__(cls, *args, **kwargs): # 产生真正的对象 return object.__new__(cls)
类的调用 obj=People('egon',18) =》Mymeta.__call__=》干了3件事 1、Mymeta.__call__函数内会先调用People内的__new__ 2、Mymeta.__call__函数内会调用People内的__init__ 3、Mymeta.__call__函数内会返回一个初始化好的对象
obj1=People('egon',18) obj2=People('egon',18) # print(obj) print(obj1.__dict__) print(obj2.__dict__)