目录

  • 一、元类介绍
  • 二、class关键字创建类的流程分析
  • 三、自定义元类
      • 需求:
    • 步骤分析:
  • 四、 _ _ new _ _ 方法
  • 五、_ _ call _ _方法
    • call 方法的应用
  • 总结:类的产生与调用
    • 类的产生:
    • 类的调用:

一、元类介绍

一切源自于一句话:python中一切皆为对象。让我们先定义一个类,然后逐步分析

class StanfordTeacher(object):school='Stanford'def __init__(self,name,age):self.name=nameself.age=agedef say(self):print('%s says welcome to the Stanford to learn Python' %self.name)

所有的对象都是实例化或者说调用类而得到的(调用类的过程称为类的实例化),比如对象t1是调用类StanfordTeacher得到的

t1=StanfordTeacher('lili',18)
print(type(t1)) #查看对象t1的类是<class '__main__.StanfordTeacher'>

如果一切皆为对象,那么类StanfordTeacher本质也是一个对象,既然所有的对象都是调用类得到的

那么StanfordTeacher必然也是调用了一个类得到的,这个类称为元类

print(type(StanfordTeacher))
# 结果为<class 'type'>,证明是调用了type这个元类而产生的StanfordTeacher,即默认的元类为type

二、class关键字创建类的流程分析

用class关键字定义的类本身也是一个对象,负责产生该对象的类称之为元类(元类可以简称为类的类),内置的元类为type

class关键字在帮我们创建类时,必然帮我们调用了元类StanfordTeacher = type(参数 1,参数2,参数3),那调用type时传入的参数是什么呢?必然是类的关键组成部分,一个类有三大组成部分

参数1、类名 class_name=‘StanfordTeacher’

参数2、基类 class_bases=(object,)

参数3、类的名称空间 class_dic,类的名称空间是执行类体代码而得到的

调用type时会依次传入以上三个参数—type( ‘StanfordTeacher’,(object,),{…} )

综上,class关键字帮我们创建一个类应该细分为以下四个过程:

1、拿到类名 'StanfordTeache

2、拿到他的基类们;

3、执行类体代码,拿到类的名称空间;

4、调用元类返回对象(返回的对象就是我们需要的“类”) :StanfordTeache = type( class_name, class_bases, class_dict )

三、自定义元类

一个类没有声明自己的元类,默认他的元类就是type,除了使用内置元类type,我们也可以通过继承type来自定义元类,这样我们用 class 关键字生成的元类使用的就是我们自定义的元类

class 类名( 继承的类名, … , metaclass = 自定义的元类名(默认为 type) ) :

由 class 关键字创建一个类的四个步骤,只有最后一步调用元类生成类是无法修改的;比如:传入的类名必须首字母大写,不大写就报错, 为了实现这样的需求,元类显然是不能满足的

需求:

传入的类名必须首字母大写,不大写就报错;类中必须有文档注释,并且文档注释不能为空

步骤分析:

1、需求分析-----People类,满足类名首字母必须大写;

2、首先需要自定义元类 Mymeta, 这样 class 类名(metaclass = Mymeta)就会对 类名首字母判断

3、使用 class 关键字创建类,相当于 类名 = Mymeta ( ‘类名’ , (需要继承的基类们,) , { 类体代码执行后的名称空间 } )

4、调用 Mymeta 类—发生"三件事":

​ 生成一个空对象,调用_ _ init _ _ 方法初始化对象,这个对象当做返回值

5、由于 Mymeta 类传入了三个参数,所以需要在 _ _ init _ _ 方法,要传入这三个参数以及空对象

6、在 _ _ init _ _ 方法中 对传入的参数进行 需求的自定义

""" 自定义元类 """
class Mymeta(type): #只有继承了type类才能称之为一个元类,否则就是一个普通的自定义类def __init__(self,class_name,class_bases,class_dic):# self.class_name = class_name# self.class_bases = class_bases# self.class_dic = class_dicsuper(Mymeta, self).__init__(class_name, class_bases, class_dic)  # 重用父类的功能if not class_name.istitle():raise TypeError('类名%s请修改为首字母大写' %class_name)if '__doc__' not in class_dic or len(class_dic['__doc__'].strip(' \n')) == 0:raise TypeError('类中必须有文档注释,并且文档注释不能为空')


四、 _ _ new _ _ 方法

调用一个类会发生"三件事",其中第一件就是–产生一个空对象,那么这个空对象是怎么产生的呢?

在调用类的时候,会首先自动触发 _ _ new _ _方法,执行里面的代码,在触发 init 方法

所以,可以推断出 执行 init 方法之前,肯定已经有方法执行完毕了

此时,我们重写 new 方法, 首先方法里什么都不写,直接执行,查看结果

class Mymeta(type):  # 只有继承了type类才能称之为一个元类,否则就是一个普通的自定义类def __init__(self, class_name, class_bases, class_dic):print(self)print(class_bases)print(self.__bases__)def __new__(cls, *args, **kwargs):passclass People(metaclass=Mymeta):"""注释"""def __init__(self, name, age):self.name = nameself.age = age

结果中 init 方法下的打印都没有执行,就直接结束了,而我们直到,new 方法是用来早一个空对象的,如果什么都没有返回,那么 init 方法也就不会调用,并且这个返回值也不能随便返回,必须要是造好的对象

这个对象需要重用元类 type 的new 方法造出来,并且会自动继承 object 类,等等的一些内置的属性都会造好

class Mymeta(type):  # 只有继承了type类才能称之为一个元类,否则就是一个普通的自定义类def __init__(self, class_name, class_bases, class_dic):print(self)print(class_bases)print(self.__bases__)def __new__(cls, *args, **kwargs):print('Mymeta--new')return super().__new__(cls, *args, **kwargs)class People(metaclass=Mymeta):"""注释"""def __init__(self, name, age):self.name = nameself.age = age

那么,现在调用一个自定义类 Mymeta 的前两步:

先调用 Mymeta 的 new 方法生成一个对象,这对象是通过 type 元类的 new 方法生成的,并且 new 方法最终返回了这个对象;

然后再调用了 Mymeta 的 init 方法初始化这个对象 ( 一般情况下要重用 type 元类的 init 方法,以上的代码只为了突出 new 方法 )

那么,就还剩下最后一步 – 返回这个对象。此时,需要思考一下:

该由谁返回?是new 方法的返回值当做调用类的最终返回值,还是 init,亦或是别的方法?

还有一个最底层的问题:为什么会先调用 new,在调用 init?是谁在控制这个流程的运行?

五、_ _ call _ _方法

调用类就是调用类的 _ _ call _ _方法,类调用后的运行逻辑,其实都是 call 方法来控制的

call 方法的应用

如果想让一个 对象 可以加括号 调用,那么需要在该对象的类中加入 call 方法,这个对象调用后的返回值就是 call 方法的返回值

由此,我们可以解决调用自定义元类 Mymeta 的最后一步返回对象的问题。

调用自定义元类 Mymeta 就是调用 type 元类的 call 方法,所以调用 Mymeta 的返回值就是使用 type 的 call 方法最终的返回值,并且这个 type 元类的 call 方法并不建议去自定义,在这里就不在过多介绍

注意:并不是继承了父类就是调用父类,这里 Mymeta 与 type 元类是特例,区别调用类与继承类

总结:类的产生与调用

1、对象 ( ) -----> 调用 类. _ _ call _ _

2、类 ( ) -----> 调用自定义元类. _ _ call _ _

3、自定义元类 ( ) -----> 调用type元类. _ _ call _ _

类的产生:

People = Mymeta ( 参数 1,2,3)—>自定义元类加括号调用 Mymeta ( ) -----> 调用type元类. _ _ call _ _方法

type元类. _ _ call _ _方法做了三件事:

1、type元类. _ _ call _ _方法 调用自定义元类 Mymeta 内的 new 方法,造出一个空对象;

2、type元类. _ _ call _ _方法 调用自定义元类 Mymeta 内的 init 方法,初始化这个对象;

3、type元类. _ _ call _ _方法 会返回这个初始化好的对象;

类的调用:

obj = People(参数…)—> People 类加括号调用 -----> 调用自定义元类 Mymeta . _ _ call _ _方法

自定义元类 Mymeta . _ _ call _ _方法做了三件事:

1、 Mymeta . _ _ call _ _方法 调用自定义元类 People 类内的 new 方法,造出一个空对象;

2、 Mymeta . _ _ call _ _方法 调用自定义元类People 类内的 init 方法,初始化这个对象;

3、 Mymeta . _ _ call _ _方法 会返回这个初始化好的对象;

生成一个对象的完整代码

class Mymeta(type):def __new__(cls, *args, **kwargs):print('type 调用了 Mymeta 的 new 方法--生成一个空对象,即 People 类')"这里调用的是 type 的 new 方法,传入参数需要注意全部传会 type 元类"return super().__new__(cls, *args, **kwargs)def __init__(self, class_name, class_bases, class_dic):print('初始化这个对象--- People 类,给 People 类添加额外的功能')super(Mymeta, self).__init__(class_name, class_bases, class_dic)  # 重用父类的功能# 自定义的类的功能if not class_name.istitle():raise TypeError('类名%s请修改为首字母大写' % class_name)if '__doc__' not in class_dic or len(class_dic['__doc__'].strip(' \n')) == 0:raise TypeError('类中必须有文档注释,并且文档注释不能为空')# 传入 Mymeta的参数:People, 以及传入People的参数def __call__(self, *args, **kwargs):"""self---<class '__main__.People'>:param args: (1,):param kwargs: {'y': 2}:return: 返回最终初始化好的代码"""print('调用了 Mymeta 的 call 方法')# 调用 People 类里的 __new__方法,生成空对象People_obj = self.__new__(self, *args, **kwargs)# 调用 People 类里的 __init__方法,初始化空对象,注意:第一个传入的参数是生成好的空对象self.__init__(People_obj, *args, **kwargs)# 给 People 类生成的对象 obj 添加额外的功能print("给 People 类生成的对象 obj 添加额外的功能")People_obj.__dict__["新增一个属性"] = None# 返回初始化好的对象return People_objclass People(metaclass=Mymeta):"""People 类的注释"""# 产生 People 类真正的对象def __new__(cls, *args, **kwargs):# 在这里就可以定制功能print('生成 People 类的空对象')print('传入的位置参数', args)print('传入的位置参数', kwargs)# 调用所继承的父类的__new__方法,这里就是 object 类,一定要传入 cls(当前这个类)"这里要区别于自定义元类的 new 方法,自定义元类调用的是 type 的 new 方法,传入参数是不一样的"return super().__new__(cls)def __init__(self, x, y=None):print("初始化 People 类的对象")self.x = xself.y = yprint("初始化 People 类的对象结束")# 调用People 类生成对象---> People()= Mymeta.__call__()
obj = People(1, y=2)
print('最终的对象字典:', obj.__dict__)

Python--元类相关推荐

  1. python 元类工厂模式_Python进阶丨如何创建你的第一个Python元类?

    摘要:通过本文,将深入讨论Python元类,其属性,如何以及何时在Python中使用元类. Python元类设置类的行为和规则.元类有助于修改类的实例,并且相当复杂,是Python编程的高级功能之一. ...

  2. 如何创建你的第一个Python元类?

    Python元类设置类的行为和规则.元类有助于修改类的实例,并且相当复杂,是Python编程的高级功能之一.通过本文,将深入讨论Python元类,其属性,如何以及何时在Python中使用元类.本文介绍 ...

  3. Python进阶丨如何创建你的第一个Python元类?

    摘要:通过本文,将深入讨论Python元类,其属性,如何以及何时在Python中使用元类. Python元类设置类的行为和规则.元类有助于修改类的实例,并且相当复杂,是Python编程的高级功能之一. ...

  4. python元类_Python元类

    python元类 Welcome to today's tutorial on python metaclass. We all know that python is an object orien ...

  5. python元类_Python基础:元类

    一.概述 Python虽然是多范式的编程语言,但它的数据模型却是 纯面向对象 的.与那些仅在语法层面声称纯OO的编程语言(如Java)相比,Python的这种纯粹性更加深入骨髓. 在Python的世界 ...

  6. Python元类详解

    文章目录 Python元类详解 Python谜团 元类的本质 调用一个类时发生了什么 再探元类 自定义元类 彩蛋:跳过python解释器 Python元类详解 元类比99%的用户所担心的魔法要更深,如 ...

  7. python元类深入理解

    1.python 中的类 在python中,类也是一个对象,只不过这个对象拥有生成实例的能力,我们一般使用class XXX来定义一个类,在python解释器执行到这个地方的时候会自动创建出这个对象, ...

  8. 深入理解 python 元类

    一.什么的元类 # 思考: # Python 中对象是由实例化类得来的,那么类又是怎么得到的呢? # 疑问: # python 中一切皆对象,那么类是否也是对象?如果是,那么它又是那个类实例化而来的呢 ...

  9. python元类编程_python元类编程

    什么叫元类?   年轻人先不要在意这些细节.我们一步一步的来! 001. oop的世界里有一句话 "万物皆对象" classPerson(object): name=Noneif ...

  10. python元类的概念_Python中的元类编程 | 学步园

    过去有这样的概念,一直没有深究它的意义.今天同事问到,刚好也好好了解下. #===============================================Python中的元类编程=== ...

最新文章

  1. 【二级java】模拟题知识点总结
  2. VS2010程序打包操作(超详细的)转
  3. No module named 'Tkinter'
  4. julia fit 函数_带有Julia中示例的flipsign()函数
  5. Java 语言 ArrayList 和 JSONArray 相互转换
  6. docker下安装mysql数据库
  7. 牛客练习-哈尔滨理工大学21级新生程序设计竞赛(同步赛)
  8. Flutter高级第1篇:ListView嵌套GridView、不同终端屏幕适配方案
  9. 常见URL字符及URL编码值
  10. python画玫瑰曲线_「风向玫瑰图」python绘制风向玫瑰图和污染物玫瑰图 - seo实验室...
  11. 程序集版本号,文件版本号及发布版本号管理
  12. 云计算的认识和看法_浅谈对云计算的认识
  13. C++中函数的重载,重写,重定义
  14. 除硬件外计算机系统不可缺少的是,银河系重约多少个太阳质量?
  15. 什么是内存泄露?该怎么排查?Java内存泄漏策略
  16. html取消ul下划线,css – 删除下划线:hover:before
  17. 关于hive统计周wau、保留率需求的几种思路
  18. 『语音信号处理』语音库 librosa 学习
  19. Python等级考试中的一道简单的血压数据处理题
  20. HTMLCSS学习笔记及其HTML5和CSS3特性

热门文章

  1. (二) Nepxion-Thunder分布式RPC集成框架 - 使用
  2. 在电脑上怎么将视频转换成GIF格式
  3. c语言中如何区分取模和除法,除法、求余和取模的区别
  4. 花书和统计课程 目录
  5. Nginx 代理PHP
  6. 机器学习(二)之无监督学习:数据变换、聚类分析
  7. 原生JS结合canvas写的一套画板,能修改画笔粗细,能修改画笔颜色,能清空画板等一系列功能
  8. 点阵图像与矢量图像的计算机记录原理,像素,分辨率,位图,失量图的含义是什么...
  9. nginx启动停止命令
  10. PiCIE: Unsupervised Semantic Segmentation Using Invariance and Equivariance in Clustering论文翻译