内置的元类: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 输出: 11 <class '__main__.DemoClass'> 12 <class 'type'> #类对象的类型都是type 13 '''
我们能够通过继承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 = name 21 22 ''' 23 输出: 24 DemoClass 25 ================ 26 () 27 ================ 28 {'__module__': '__main__', '__init__': <function DemoClass.__init__ at 0x00000144C9943B70>, '__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 = name 22 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 才是demo 15 # 2,调用类对象的init 16 17 print("2") 18 self.__init__(obj,*args,**kwargs) 19 print("4") 20 print(args,kwargs) 21 #,3 返回obj 22 return obj 23 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 = name 34 if __name__ == "__main__": 35 demo = DemoClass("tom") #DemoClass 其实是个类对象,然后它加() 会调用__call__ 36 print(id(DemoClass)) 37 ''' 38 输出: 39 1 40 1817911787480 41 2 42 3 43 4 44 ('tom',) {} 45 1817911787480 46 '''
所以,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 =200 10 11 class Demo2(Demo1): 12 n = 300 13 14 class Demo3(Demo2,metaclass=Mymeta): 15 n = 400 16 17 def __init__(self,name): 18 self.name = name 19 print(Demo3.n) 20 #查找n 的顺序: 21 #先类对象层 :Demo3 ->Demo2-->Demo1-->object 22 #再元类层: Mymeta -->type 23 24 25 # 注:object 也是个类对象 26 print(type(object)) 27 #<class 'type'>
下面是产生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 = 300 13 # def __new__(cls, *args, **kwargs): 14 # print("Demo1_new") 15 class Demo3(Demo2,metaclass=Mymeta): 16 n = 400 17 18 def __init__(self,name): 19 self.name = name 20 21 Demo3("tom") 22 ''' 23 输出:True 24 '''
结论: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 = 200 13 14 def __init__(self,name): 15 self.name = name 16 #产生类对象Demo 的过程就是在调Mymeta ,而Mymeta 也是type类的一个对象, 17 #那么Mymeta 之所以可以调用,一定是因为元类type中有一个__call__方法
# class type:
# def __call__(self, *args, **kwargs): #self=<class '__main__.Mymeta'>
# 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 = 200 14 def __init__(self,name): 15 self.name = name 16 17 demo = Demo("tom") #这会找到type 中的__call__方法 18 ''' 19 type 中的方法应该是: 20 # class type: 21 # def __call__(self, *args, **kwargs): #self=<class '__main__.Mymeta'> 22 # obj=self.__new__(self,*args,**kwargs) # 产生Mymeta的一个对象 23 # self.__init__(obj,*args,**kwargs) 24 # return obj 25 ''' 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 = 18 15 def test(self): 16 pass 17 18 print(Demo.__dict__) 19 ''' 20 输出:{'__weakref__': <attribute '__weakref__' of 'Demo' objects>, '__module__': '__main__', 'AGE': 18, 'test': <function Demo.test at 0x000002C156163B70>, '__doc__': None, 'NAME': 'tom', '__dict__': <attribute '__dict__' of 'Demo' objects>} 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 obj 10 11 class DemoClass(metaclass=Mymeta): 12 salary = 1000 13 def test(self): 14 pass 15 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__': <attribute '__weakref__' of 'DemoClass' objects>, '__dict__': <attribute '__dict__' of 'DemoClass' objects>, 'test': <function DemoClass.test at 0x000002B2B3A03BF8>} 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()] = v 10 return obj 11 12 class DemoClass(object,metaclass=Mymeta): 13 salary = 1000 14 def test(self): 15 pass 16 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, in <module> 23 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 = name 11 self.age= age 12 self.sex = sex 13 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