内置的元类:type ,它是所有类对象的类!
一个类对象没有声明它的元类,那么它的默认元类就是type .
1 class DemoClass(): 2 def __init__(self,name): 3 self.name = name 4 5 if __name__ == "__main__": 6 demo = DemoClass("tom") 7 print(type(demo)) 8 print(type(DemoClass)) 9 '''10 输出:1112 #类对象的类型都是type13 '''
我们能够通过继承type 来自定义元类,然后其他类对象创建的时候就可以用metaclass 来指定自定义元类。
类对象的创建流程:(用自定义元类创建类对象)
1 class Mymeta(type): #自定义元类 2 def __init__(self,class_name,class_bases,class_dict): 3 super().__init__(class_name,class_bases,class_dict) #重用父类的init 4 print(class_name,"\n================") 5 print(class_bases,"\n================") 6 print(class_dict,"\n================") 7 8 if class_name.islower(): 9 raise TypeError("类对象名{}请修改为驼峰式".format(class_name))10 11 if '__doc__' not in class_dict or len(class_dict['__doc__'].strip('\n'))==0:12 raise TypeError("类中要有注释,且注释不能为空")13 #类对象的创建14 #DemoClass = Mymeta(name,(object,),{})15 class DemoClass(metaclass=Mymeta): #这时类对象DemoClass 的类就不是type而是Mymeta了16 '''17 DemoClass 的注释18 '''19 def __init__(self,name):20 self.name = name21 22 '''23 输出:24 DemoClass 25 ================26 () 27 ================28 {'__module__': '__main__', '__init__':, '__doc__': '\n DemoClass 的注释\n ', '__qualname__': 'DemoClass'} 29 ================30 '''
我们知道 类对象+() 会触发元类中的__call__()方法,
1 class Mymeta(type): #自定义元类 2 def __init__(self,class_name,class_bases,class_dict): 3 super().__init__(class_name,class_bases,class_dict) #重用父类的init 4 5 if class_name.islower(): 6 raise TypeError("类对象名{}请修改为驼峰式".format(class_name)) 7 8 if '__doc__' not in class_dict or len(class_dict['__doc__'].strip('\n'))==0: 9 raise TypeError("类中要有注释,且注释不能为空")10 def __call__(self, *args, **kwargs):11 print(args,kwargs)12 13 14 #类对象的创建15 #DemoClass = Mymeta(name,(object,),{})16 class DemoClass(metaclass=Mymeta): #这时类对象DemoClass 的类就不是type而是Mymeta了17 '''18 DemoClass 的注释19 '''20 def __init__(self,name):21 self.name = name22 if __name__ == "__main__":23 DemoClass("tom") #DemoClass 其实是个类对象,然后它加() 会调用__call__24 25 '''26 输出:27 ('tom',) {}28 '''
默认的类对象 实例化的过程会做三件事:
1、产生一个空对象obj
2、调用__init__方法初始化对象obj
3、返回初始化好的obj
所以,我们也自己加上这三件事:
1 class Mymeta(type): #自定义元类 2 def __init__(self,class_name,class_bases,class_dict): 3 super().__init__(class_name,class_bases,class_dict) #重用父类的init 4 print("1") 5 if class_name.islower(): 6 raise TypeError("类对象名{}请修改为驼峰式".format(class_name)) 7 8 if '__doc__' not in class_dict or len(class_dict['__doc__'].strip('\n'))==0: 9 raise TypeError("类中要有注释,且注释不能为空")10 #产生类对象之后,加括号会调__call__11 def __call__(self, *args, **kwargs):12 print(id(self))13 # 1,产生空对象14 obj = self.__new__(self) #self是类对象DemoClass 不是demo ,obj 才是demo15 # 2,调用类对象的init16 17 print("2")18 self.__init__(obj,*args,**kwargs)19 print("4")20 print(args,kwargs)21 #,3 返回obj22 return obj23 24 #类对象的创建25 #DemoClass = Mymeta(name,(object,),{})26 class DemoClass(metaclass=Mymeta): #这时类对象DemoClass 的类就不是type而是Mymeta了27 '''28 DemoClass 的注释29 '''30 31 def __init__(self,name): #__init__() 是类对象的属性,所以在元类__call__中,self才能调它.32 print("3")33 self.name = name34 if __name__ == "__main__":35 demo = DemoClass("tom") #DemoClass 其实是个类对象,然后它加() 会调用__call__36 print(id(DemoClass))37 '''38 输出:39 140 181791178748041 242 343 444 ('tom',) {}45 181791178748046 '''
所以,demo = DemoClass("tom") 这个过程不是自动的调init() 而是先调__call__() 然后,在__call__中会回头调的init() 。
属性查找:
1 class Mymeta(type): #只有继承type 才是自定义元类,否则是普通的类 2 n = 100 3 def __call__(self, *args, **kwargs): 4 obj = self.__new__(self) 5 self.__init__(obj,*args,**kwargs) 6 return obj 7 8 class Demo1(object): #继承object 9 n =20010 11 class Demo2(Demo1):12 n = 30013 14 class Demo3(Demo2,metaclass=Mymeta):15 n = 40016 17 def __init__(self,name):18 self.name = name19 print(Demo3.n)20 #查找n 的顺序:21 #先类对象层 :Demo3 ->Demo2-->Demo1-->object22 #再元类层: Mymeta -->type 23 24 25 # 注:object 也是个类对象26 print(type(object))27 #
下面是产生obj的方法 类对象的方法__new__ 的查找:
1 class Mymeta(type): #只有继承type 才是自定义元类,否则是普通的类 2 n = 100 3 def __call__(self, *args, **kwargs): 4 obj = self.__new__(self) 5 print(self.__new__ is object.__new__) 6 7 class Demo1(object): #继承object 8 n =200 9 # def __new__(cls, *args, **kwargs):10 # print("Demo1_new")11 class Demo2(Demo1):12 n = 30013 # def __new__(cls, *args, **kwargs):14 # print("Demo1_new")15 class Demo3(Demo2,metaclass=Mymeta):16 n = 40017 18 def __init__(self,name):19 self.name = name20 21 Demo3("tom")22 '''23 输出:True24 '''
结论:Mymeta下的__call__里的self.__new__在Demo3、Demo2、Demo1里都没有找到__new__的情况下,会去找object里的__new__,而object下默认就有一个__new__,所以即便是之前的类均未实现__new__,也一定会在object中找到一个,根本不会、也根本没必要再去找元类Mymeta->type中查找__new__
在元类的__call__中也可以用object.__new__(self)去造对象
但还是推荐在__call__中使用self.__new__(self)去创造空对象,因为这种方式会检索三个类Demo3->Demo2->Demo1,而object.__new__则是直接跨过了他们三个
补:
1 class Mymeta(type): #只有继承type 才是自定义元类,否则是普通的类 2 n = 100 3 def __new__(cls, *args, **kwargs): 4 obj = type.__new__(cls,*args,**kwargs) #必须按照这样传值 cls 为类对象 5 print(obj.__dict__) 6 #return obj #只有返回obj 才会调下面的init 7 return 123 8 def __init__(self,class_name,class_bases,class_dict): 9 print("run....")10 #Demo = Mymeta()11 class Demo(object,metaclass=Mymeta):12 n = 20013 14 def __init__(self,name):15 self.name = name16 #产生类对象Demo 的过程就是在调Mymeta ,而Mymeta 也是type类的一个对象,17 #那么Mymeta 之所以可以调用,一定是因为元类type中有一个__call__方法
# class type:# def __call__(self, *args, **kwargs): #self=# obj=self.__new__(self,*args,**kwargs) # 产生Mymeta的一个对象# self.__init__(obj,*args,**kwargs) # return obj
1 class Mymeta(type): #只有继承type 才是自定义元类,否则是普通的类 2 n = 100 3 def __new__(cls, *args, **kwargs): 4 obj = type.__new__(cls,*args,**kwargs) #cls 是Mymeta这个类对象 5 print(obj.__dict__) 6 return obj 7 8 def __init__(self,class_name,class_bases,class_dict): 9 print("run....")10 11 #把自定义的数据属性都变成大写12 class Demo(object,metaclass=Mymeta):13 n = 20014 def __init__(self,name):15 self.name = name16 17 demo = Demo("tom") #这会找到type 中的__call__方法18 '''19 type 中的方法应该是:20 # class type:21 # def __call__(self, *args, **kwargs): #self=22 # obj=self.__new__(self,*args,**kwargs) # 产生Mymeta的一个对象23 # self.__init__(obj,*args,**kwargs) 24 # return obj25 '''26 print(demo.n)
练习1:将类属性变为全大写:
1 class Mymeta(type): #只有继承type 才是自定义元类,否则是普通的类 2 def __new__(cls, class_name,class_bases,class_dict): #type中的__call__ 会调它 3 update_attrs = {} 4 for k,v in class_dict.items(): 5 if not callable(v) and not k.startswith('__'): 6 update_attrs[k.upper()]=v 7 else: 8 update_attrs[k] =v 9 return type.__new__(cls,class_name,class_bases,update_attrs)#生成的Demo对象10 11 #把类中的数据属性都变成大写12 class Demo(object,metaclass=Mymeta):13 name = "tom"14 age = 1815 def test(self):16 pass17 18 print(Demo.__dict__)19 '''20 输出:{'__weakref__':, '__module__': '__main__', 'AGE': 18, 'test': , '__doc__': None, 'NAME': 'tom', '__dict__': }21 '''
练习二:类中无需__init__方法:
1 class Mymeta(type): #只有继承type 才是自定义元类,否则是普通的类 2 def __call__(self, *args, **kwargs): 3 if args: 4 raise TypeError("必须以键值对传参") 5 obj = object.__new__(self) 6 7 for k,v in kwargs.items(): 8 obj.__dict__[k.upper()] = v 9 return obj10 11 class DemoClass(metaclass=Mymeta):12 salary = 100013 def test(self):14 pass15 16 17 p = DemoClass(name = "tom",age = 18)18 print(p.__dict__)19 print(DemoClass.__dict__)20 '''21 {'AGE': 18, 'NAME': 'tom'}22 {'salary': 1000, '__module__': '__main__', '__doc__': None, '__weakref__':, '__dict__': , 'test': }23 '''
1 class Mymeta(type): #只有继承type 才是自定义元类,否则是普通的类 2 def __call__(self, *args, **kwargs): 3 print(args,kwargs) 4 if args: 5 raise TypeError("必须以键值对传参") 6 obj = object.__new__(self) 7 8 for k,v in kwargs.items(): 9 obj.__dict__[k.upper()] = v10 return obj11 12 class DemoClass(object,metaclass=Mymeta):13 salary = 100014 def test(self):15 pass16 p = DemoClass(18,name = "tom",age = 18,)17 print(p.__dict__)18 print(DemoClass.__dict__)19 '''20 Traceback (most recent call last):21 (18,) {'name': 'tom', 'age': 18}22 File "C:/Users/Administrator/Desktop/test/m1/testtest.py", line 16, in23 p = DemoClass(18,name = "tom",age = 18,)24 File "C:/Users/Administrator/Desktop/test/m1/testtest.py", line 5, in __call__25 raise TypeError("必须以键值对传参")26 TypeError: 必须以键值对传参27 '''
三:属性设置为隐藏属性:
1 class Mymeta(type): 2 def __call__(self, *args, **kwargs): 3 obj = self.__new__(self) 4 self.__init__(obj,*args,**kwargs) 5 obj.__dict__ ={ "_{}__{}".format(self.__name__,k):v for k,v in obj.__dict__.items() } 6 return obj 7 8 class DemoClass(object,metaclass=Mymeta): 9 def __init__(self,name,age,sex):10 self.name = name11 self.age= age12 self.sex = sex13 14 15 obj=DemoClass('tom',18,'male')16 print(obj.__dict__)17 setattr(obj,"salary",15054)18 print(obj.__dict__)19 '''20 {'_DemoClass__age': 18, '_DemoClass__sex': 'male', '_DemoClass__name': 'tom'}21 {'_DemoClass__age': 18, '_DemoClass__sex': 'male', '_DemoClass__name': 'tom', 'salary': 15054}22 '''
参考博客:https://www.cnblogs.com/linhaifeng/articles/8029564.html