python中的元类metaclass详情


Posted in Python onMay 30, 2022

动机

python语言因为工作偏向于 AI ,所以对于这门语言还停留在表面,对于 python 深层并没有接触到。

今天来聊一聊元类(metaclass) ,想必大多数人都或多或少听过元编程这个词,但是对于元编程是什么以及如何应用元编程熟悉应该不多,在 python 中的 metaclass 就是帮助 developer 实现元编程,因此产生一个想法

最近时间还算宽裕,所以想要文章认真弄一弄

从一个问题引出 MetaClass

在 python 语言中,并没有函数重载,我们下面通过一个具体例子来说明。

class A():
    def f(self, x:int):
        print('A.f int overload',self,x)
    def f(self,x:str):
        print('A.f str overload',self,x)
    def f(self,x,y):
        print('A.f two arg overload',self,x,y)
if __name__ == "__main__":
    a = A()
    a.f(1)

当执行上面代码我们会得到一个错误信息,实例化 A 类后,调用实例的 f 方法,因为在 python 语言中没有重装方法,所以 def f(self,x:str) 会覆盖之前的 def f(self, x:int), 而 def f(self,x,y) 方法会覆盖于 def f(self,x:str) 方法,所以当通过传入 1 一个参数,不会调用 def f(self,x:int) 而是调用 def f(self,x,y) 方法。

TypeError: f() missing 1 required positional argument: 'y'

那么什么是正确的姿势解决这个问题呢? 这里先不急于给出答案,当我们介绍完 metaclass 后,答案就自然浮出水面。

Metaclass 编程

想要了解 Metaclass 也就是元类,meta 在英文中超越的意思,也就是 Metaclass 是高级于 class,用于创建 class 的 class。有的时候我们需要控制类创建过程,通常创建类工作是由 type 完成的,因为 type 直接设计到 c,我们希望在 type 创建类过程插入一些自定义的东西,所以引入了 Metaclass 让某一个类创建工作不再由 type 来实现,而是由指定 class 来实现

在 python 中,我们可以通过 class 来实例化对象,不过这里要说在 python 中 class 其实也是对象。既然 class 也是对象,那么 class 的类型又是什么呢

class A:
    a = 1
    b = "hello"
    def f(self):
        return 12
def main():
    print(f'{type(2)=}')
    print(f'{type("hello")=}')
    print(f'{type([])=}')
    print(f'{type(A())=}')
if __name__ == "__main__":
    main()

输出一下 2、hello 、空数组和 A 类实例的类型,结果发现他们类别分别为 int、str、list 和 A 类别。其实他们也是对象,既然是对象,那么就会有一个 class 用于创建这个类别。

type(2)=<class 'int'>
type("hello")=<class 'str'>
type([])=<class 'list'>
type(A())=<class '__main__.A'>

接下来我们就看一下这些 class(int,str,list) 那么这些对象又是什么类别呢

class A:
    a = 1
    b = "hello"
    def f(self):
        return 12
if __name__ == "__main__":
    print(f'{type(int)=}')
    print(f'{type(str)=}')
    print(f'{type(list)=}')
    print(f'{type(A)=}')
type(int)=<class 'type'>
type(str)=<class 'type'>
type(list)=<class 'type'>
type(A)=<class 'type'>

不难看出多有 class 的类型都是 type ,例如数字 2 的 int 的一个实例,而 int 又是 type 的一个实例。

如果大家从类似 java 这些语言开始,然后再去学习 python 可能会有这样疑问,在 python 中 type 和 class 有什么区别,他们不都是类型吗? 其实答案就是这两者在 python3 中并没有区别,可以将他们看做一个东西。

def main():
    x = int()
    print(f'{x=}')
    B = type('B',(),{})
    print(f'{B=}')
if __name__  == "__main__":
    main()

不过如果进一步深入研究,两种 class 和 type 在字面上,是不同两样东西,class 作为关键字来定义类型时,是调用其构造器来做了一些初始化的工作。

def main():
    x = int()
    print(f'{x=}')
    B = type('B',(),{})
    print(f'{B=}')
if __name__  == "__main__":
    main()

我们可以这样来定义一个类型

x=0
B=<class '__main__.B'>

可以用 class 方式来定义一个类 A,然后我们在用 type 方式来创建一个类,type 接受 3 个参数分别是类的名称,这里接受的字符串类型的名称、以及该类的基类,是组元的形式,接下来是就是一个属性,属性是字典形式数据,键是属性名称,值是属性值。

class A:
    a = 2
    b = 'hello'

    def f(self):
        return 12

下面我们用 make_A 来创建一个类, 这里使用 type 来定义一个类

def make_A():
    name = 'A'
    bases = ()
    a = 2
    b = 'hello'

    def f(self):
        return 12
    namespace = {'a':a,'b':b,'f':f}
    A = type(name,bases,namespace)
    return

通过 type 创建类时候需要传入类名称 A 然后 base 是一个要创建类 A 的基类,namescpace 是类属性,是 dict 形式,键是属性名称,而值是属性值。

def make_A_more_accurate():
    name = 'A'
    bases = ()
    namespace = type.__prepare__(name,bases)
    body = (
"""
a = 1
b = 'hello'

def f(self):
    return 12
"""
    )
    exec(body,globals(),namespace)
    A = type(name,bases,namespace)
    return A

metaclass 是继承于 type,那么 metaclass 的工作也是用于创建 class,我们可以在 metaclass 中做一些自定义的事情,

这里可能比较难理解是 __prepare__ 上网找到关于 __prepare__ 解释,暂时说一下自己认识,可能有点浅,感觉就是为类创建了一个局部的作用域。

namespace = type.__prepare__(name,bases)
print(namespace)

type.__prepare__ 应该是返回一个局部命名空间,

exec(body,globals(),namespace)

class Tut:
    ...
tut = Tut()
print(f'{type(tut)=}')
print(f'{type(Tut)=}')

上面例子定义一个类,然后实例化 Tut 类得到对象 tut,接下来分别输出 tut 和 Tut 类型

type(tut)=<class '__main__.Tut'>
type(Tut)=<class 'type'>

不难看出 tut 是 Tut 的实例,而 Tut 是 type 的对象

class TutMetaClass(type):
    ...
class Tut(metaclass=TutMetaClass):
    ...

然后我们定义一个 TutMetaClass 继承于 type,然后将 Tut 类的 metaclass 指向 TutMetaClass ,然后 tut 类型为 Tut,而 Tut 类型为 TutMetaClass 类型

type(tut)=<class '__main__.Tut'>
type(Tut)=<class '__main__.TutMetaClass'>

到此这篇关于 python 中的 元类metaclass详情的文章就介绍到这了!


Tags in this post...

Python 相关文章推荐
Python的多态性实例分析
Jul 07 Python
python之Character string(实例讲解)
Sep 25 Python
Python语言检测模块langid和langdetect的使用实例
Feb 19 Python
解决Python找不到ssl模块问题 No module named _ssl的方法
Apr 29 Python
Python opencv实现人眼/人脸识别以及实时打码处理
Apr 29 Python
PyQT实现菜单中的复制,全选和清空的功能的方法
Jun 17 Python
将labelme格式数据转化为标准的coco数据集格式方式
Feb 17 Python
python实现小程序推送页面收录脚本
Apr 20 Python
django model 条件过滤 queryset.filter(**condtions)用法详解
May 20 Python
详解Python 函数参数的拆解
Sep 02 Python
基于OpenCV的网络实时视频流传输的实现
Nov 15 Python
django中ImageField的使用详解
Dec 21 Python
Python自动化实战之接口请求的实现
Python中的 enumerate和zip详情
May 30 #Python
python如何为list实现find方法
May 30 #Python
python实现商品进销存管理系统
May 30 #Python
python如何查找列表中元素的位置
May 30 #Python
Python数组变形的几种实现方法
May 30 #Python
Python Matplotlib绘制动画的代码详解
You might like
php自定义函数截取汉字长度
2014/05/15 PHP
浅析PHP微信支付通知的处理方式
2014/05/25 PHP
PHP微框架Dispatch简介
2014/06/12 PHP
PHP编程快速实现数组去重的方法详解
2017/07/22 PHP
Gambit vs ForZe BO3 第一场 2.13
2021/03/10 DOTA
Javascript string 扩展库代码
2010/04/09 Javascript
jquery动画3.创建一个带遮罩效果的图片走廊
2012/08/24 Javascript
js实现的点击div区域外隐藏div区域
2014/06/30 Javascript
JavaScript中的console.profile()函数详细介绍
2014/12/29 Javascript
JS实现当前页居中分页效果的方法
2015/06/18 Javascript
jQuery仅用3行代码实现的显示与隐藏功能完整实例
2015/10/08 Javascript
跟我学习javascript的函数和函数表达式
2015/11/16 Javascript
js+canvas简单绘制圆圈的方法
2016/01/28 Javascript
解决vuejs 使用value in list 循环遍历数组出现警告的问题
2018/09/26 Javascript
9种方法优化jQuery代码详解
2020/02/04 jQuery
Vue如何实现监听组件原生事件
2020/07/03 Javascript
react的hooks的用法详解
2020/10/12 Javascript
二种python发送邮件实例讲解(python发邮件附件可以使用email模块实现)
2013/12/03 Python
python文件与目录操作实例详解
2016/02/22 Python
Flask-Mail用法实例分析
2018/07/21 Python
简单易懂Pytorch实战实例VGG深度网络
2019/08/27 Python
Django 对IP访问频率进行限制的例子
2019/08/30 Python
Python实现基于socket的udp传输与接收功能详解
2019/11/15 Python
python如何求圆的面积
2020/07/01 Python
Pycharm2020.1安装中文语言插件的详细教程(不需要汉化)
2020/08/07 Python
CSS3实现菜单悬停效果
2020/11/17 HTML / CSS
英国顶级珠宝品牌之家:John Greed
2018/06/09 全球购物
澳大利亚运动鞋零售商:The Athlete’s Foot
2018/11/04 全球购物
执行力心得体会
2013/12/31 职场文书
就业自我评价
2014/02/04 职场文书
《满井游记》教学反思
2014/02/26 职场文书
学校创先争优活动总结
2014/08/28 职场文书
一年级班主任工作总结2014
2014/11/08 职场文书
与死神共舞观后感
2015/06/15 职场文书
KTV员工管理制度
2015/08/06 职场文书
CentOS8.4安装Redis6.2.6的详细过程
2021/11/20 Redis