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语言技巧之三元运算符使用介绍
Mar 04 Python
python实现在目录中查找指定文件的方法
Nov 11 Python
人机交互程序 python实现人机对话
Nov 14 Python
Django REST为文件属性输出完整URL的方法
Dec 18 Python
Python的SimpleHTTPServer模块用处及使用方法简介
Jan 22 Python
使用pandas对两个dataframe进行join的实例
Jun 08 Python
python 数字类型和字符串类型的相互转换实例
Jul 17 Python
对python制作自己的数据集实例讲解
Dec 12 Python
python抓取需要扫微信登陆页面
Apr 29 Python
如何利用python web框架做文件流下载的实现示例
Jun 02 Python
python字符串拼接.join()和拆分.split()详解
Nov 23 Python
使用opencv-python如何打开USB或者笔记本前置摄像头
Jun 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
Symfony2创建页面实例详解
2016/03/18 PHP
js Html结构转字符串形式显示代码
2011/11/15 Javascript
用正则表达式替换图片地址img标签
2013/11/22 Javascript
js抽奖实现随机抽奖代码效果
2013/12/02 Javascript
解决extjs grid 不随窗口大小自适应的改变问题
2014/01/26 Javascript
IE6-IE9中tbody的innerHTML不能赋值的解决方法
2014/06/05 Javascript
对 jQuery 中 data 方法的误解分析
2014/06/18 Javascript
Node.js中的模块机制学习笔记
2014/11/04 Javascript
node.js中的path.resolve方法使用说明
2014/12/08 Javascript
JS判断网页广告是否被浏览器拦截过滤的代码
2015/04/05 Javascript
Javascript中的getUTCHours()方法使用详解
2015/06/10 Javascript
微信小程序 欢迎界面开发的实例详解
2016/11/30 Javascript
webpack2.0搭建前端项目的教程详解
2017/04/05 Javascript
详解Angular 中 ngOnInit 和 constructor 使用场景
2017/06/22 Javascript
详解vue2.0 使用动态组件实现 Tab 标签页切换效果(vue-cli)
2017/08/30 Javascript
微信小程序模版渲染详解
2018/01/26 Javascript
Bootstrap 时间日历插件bootstrap-datetimepicker配置与应用小结
2019/05/28 Javascript
JS检测浏览器开发者工具是否打开的方法详解
2020/10/02 Javascript
[42:20]Secret vs Liquid 2019国际邀请赛小组赛 BO2 第二场 8.15
2019/08/17 DOTA
合并Excel工作薄中成绩表的VBA代码,非常适合教育一线的朋友
2009/04/09 Python
Python查找函数f(x)=0根的解决方法
2015/05/07 Python
Python中逗号的三种作用实例分析
2015/06/08 Python
理解Python中的绝对路径和相对路径
2017/08/30 Python
Python将多个excel表格合并为一个表格
2021/02/22 Python
Python利用sqlacodegen自动生成ORM实体类示例
2019/06/04 Python
Python 虚拟空间的使用代码详解
2019/06/10 Python
Python Django框架url反向解析实现动态生成对应的url链接示例
2019/10/18 Python
python爬虫添加请求头代码实例
2019/12/28 Python
windows python3安装Jupyter Notebooks教程
2020/04/13 Python
Python爬虫HTPP请求方法有哪些
2020/06/03 Python
详解html5 canvas常用api总结(二)--绘图API
2016/12/14 HTML / CSS
AmazeUI 模态窗口的实现代码
2020/08/18 HTML / CSS
Monnier Freres中文官网:法国领先的奢侈品配饰在线零售商
2017/11/01 全球购物
德国Discount-Apotheke中文官网:DC德式康线上药房
2020/02/18 全球购物
出纳的岗位职责
2013/11/09 职场文书
有关朝花夕拾的读书笔记
2015/06/29 职场文书