博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
元类type
阅读量:5364 次
发布时间:2019-06-15

本文共 10113 字,大约阅读时间需要 33 分钟。

 

内置的元类: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 
12
#类对象的类型都是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, in 
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 = 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

 

转载于:https://www.cnblogs.com/zach0812/p/11316483.html

你可能感兴趣的文章
antiSMASH数据库:微生物次生代谢物合成基因组簇查询和预测
查看>>
UNICODE与ANSI的区别
查看>>
nginx 配置实例
查看>>
Flutter - 创建底部导航栏
查看>>
ASP.NET MVC 教程-MVC简介
查看>>
SQL Server索引 - 聚集索引、非聚集索引、非聚集唯一索引 <第八篇>
查看>>
转载:详解SAP TPM解决方案在快速消费品行业中的应用
查看>>
Android OpenGL ES 开发(N): OpenGL ES 2.0 机型兼容问题整理
查看>>
项目中用到的技术及工具汇总(持续更新)
查看>>
【算法】各种排序算法测试代码
查看>>
HDU 5776 Sum
查看>>
201521123044 《Java程序设计》第9周学习总结
查看>>
winfrom 图片等比例压缩
查看>>
人工智能实验报告一
查看>>
用LR12录制app,用LR11跑场景,无并发数限制,已试验过,可行!
查看>>
python 多线程就这么简单(转)
查看>>
oracle 简述
查看>>
ajax如何向后台传递数组,在后台该如何接收的问题(项目积累)
查看>>
Solr之java实现增删查操作
查看>>
httpClient连接工具类实测可用
查看>>