首页 > 编程语言 > python 学习-打开潘多拉的魔盒-元类(metaclass)学习

python 学习-打开潘多拉的魔盒-元类(metaclass)学习

时间:2023-05-11 13:00:10浏览次数:45  
标签:__ name python type 元类 print new metaclass class

前言

在 Python 里面大家都比较熟悉了,通过 class 关键字创建一个类,这是通过硬编码来实现的。
那么如何动态创建一个类呢,如果给一批数据,让它动态生成一个类?

学习警告:不要轻易打开潘多拉的魔盒,潘多拉出于好奇打开一个魔盒, 释放出人世间的所有邪恶:贪婪、虚无、诽谤、嫉妒、痛苦等等,当她再盖上盒子时,只剩下希望在里面。
不要轻易去开启python的 黑魔法--元类(metaclass)学习,可能会有2个极端

  • 打开之后,如果你能驾驭,会发现无所不能,真正掌握了面向对象的精髓,可以无所不能实现你想要的任何功能。
  • 如果你出于好奇是尝试它,恶魔被释放出来,可能会让你越来越痛苦,最后可能会怀疑自己的学习能力,逐渐失去学习的乐趣。

类与实例

先来理解一个非常简单的例子

class People:
    name = "zhangsan"
    age = 22


p = People()
print(p.name)
print(p.age)

上面代码 People 是一个类, p是 People 类的实例。

接着用 type 查看对象

print(type(p))  # <class '__main__.People'>
print(type(People))  # <class 'type'>

p 是 People 类的实例
People 是一个类,它是 type 的实例,也就是说 People 类是 type 创建的一个实例。
type 就是一个元类(metaclass),简单的理解,元类就是创建类的类。

再举个简单例子

  • 数字123 是一个实例,它是 Int 类的实例, Int类又是type 创建的。
  • 字符串“adc” 是一个实例,它是 Str 类的实例,Str 类又是 type 创建的。
x = 123
print(type(x))    # <class 'int'>
print(type(int))  # <class 'type'>

y = "abc"
print(type(y))    # <class 'str'>
print(type(str))  # <class 'type'>

学到这里也就理解了,python是面向对象的编程语言,python里面的str, int 等class 创建的类,都是type 类创建的,type 就是一个创建类的元类(metaclass)。
str, int 等class 创建的类都是 type 类的实例。

用一个图来表示对象(obj,或叫实例)、类(class)、元类(Metaclass)的关系。

可以这样理解:张三是人类的一个实例,人类是上帝创造的,那么人类是上帝的一个实例, type 就是 python 里面的上帝

type 动态创建类

type 创建类的部分源码

  def __init__(cls, what, bases=None, dict=None): # known special case of type.__init__
      """
      type(object_or_name, bases, dict)
      type(object) -> the object's type
      type(name, bases, dict) -> a new type
      # (copied from class doc)
      """
      pass

基本语法如下:

type(name of the class, 
  tuple of the parent class (for inheritance, can be empty), 
  dictionary containing attributes names and values)

传三个参数:

  • class 类的名称, 字符串类型
  • bases 是需要继承的类,默认继承object,可以为空,类型传元组
  • dict 字典类型,传类的属性和方法

接着我们用 type 动态创建一个类

# 通过 type 创建一个猫类
Cat = type("Cat", (object, ), {"name": "hello kitty", "age": 2})
c = Cat()
print(c.name)
print(c.age)
print(type(c))   # <class '__main__.Cat'>
print(type(Cat)) # <class 'type'>

Cat 就是 type 创建的一个类,等价于自己写的class Cat, 它是type 类的实例
c 是 Cat 类的实例。

学到这,就是掌握了使用 type 动态创建类的入门学习了~

自定义元类(metaclass)

如果想把一个类设计成 MetaClass 元类,其必须符合以下条件:

  • 必须显式继承自 type 类;
  • 类中需要定义并实现 __new__() 方法,该方法一定要返回该类的一个实例对象,因为在使用元类创建类时,该 __new__() 方法会自动被执行,用来修改新建的类。


class DemoMetaClass(type):
    def __init__(cls, what, bases=None, dict=None):
        """
        初始化,四个参数
        """
        print("metaclass 创建类初始化。。。。")
        super().__init__(what, bases, dict)

    def __new__(cls, name, bases, attrs):
        """
         创建类,四个参数
           cls 代表动态修改的类
           name 代表动态修改的类名
           bases 代表被动态修改的类的所有父类
           attr 代表被动态修改的类的所有属性、方法组成的字典
        """
        # 动态为该类添加一个name属性
        attrs['name'] = "zhangsan"
        attrs['age'] = lambda x: 20
        return super().__new__(cls, name, bases, attrs)


# 定义一个类,指定metaclass 元类创建,而不是由 type 创建
class NewDemo(object, metaclass=DemoMetaClass):
    pass

以上代码直接执行,会看到打印结果

metaclass 创建类初始化。。。。

上面代码中 DemoMetaClass 是一个类, 该类继承自 type 类,并且内部实现了 __new__() 方法,因此 DemoMetaClass 是一个元类。
NewDemo 类不再由默认的type 创建了,而是由自己写的 DemoMetaClass 来创建。

当使用class NewDemo 创建类时,DemoMetaClass 元类就会触发__init__ 初始化 和 __new__ 创建类。

同样的道理,当我们写一个类, 继承了NewDemo (它由DemoMetaClass 创建)

class Hello(NewDemo):
    pass

它也会触发元类 __new__ 创建类 和 __init__ 初始化。

还可以由 type 动态创建类

World = type("World", (NewDemo, ), {})
w = World()
print(w.name)     # zhangsan
print(w.age())    # 20

它也会触发元类 __new__ 创建类 和 __init__ 初始化 。
学到这,大家会发现目前自己创建的元类已经改变了python 创建类的默认操作,潘多拉的魔盒被你悄悄打卡了~~

掌握 __init____new__

如果创建的class 类里面也有__init____new__, 看下执行过程是怎样的


class DemoMetaClass(type):
    def __init__(cls, what, bases=None, dict=None):
        """
        初始化,四个参数
        """
        print("metaclass 创建类初始化。。。。")
        super().__init__(what, bases, dict)

    def __new__(cls, name, bases, attrs):
        """
         创建类,四个参数
           cls 代表动态修改的类
           name 代表动态修改的类名
           bases 代表被动态修改的类的所有父类
           attr 代表被动态修改的类的所有属性、方法组成的字典
        """
        # 动态为该类添加一个name属性
        attrs['name'] = "zhangsan"
        attrs['age'] = lambda x: 20
        print(f"metaclass __new__: {cls}, {name}, {bases}, {attrs}")
        return super().__new__(cls, name, bases, attrs)


# 定义一个类,指定metaclass 元类创建,而不是由 type 创建
class NewDemo(object, metaclass=DemoMetaClass):
    def __init__(self, x):
        self.x = x
        print(f"class __init__ : {self.x}")

    def __new__(cls, *args, **kwargs):  # 通过 new 来实例化 __init__.new 是用来创建实例的。
        print(f"class __new__:{cls}, {args}, {kwargs}")
        return object.__new__(cls)


print("------------------------------------------")
demo = NewDemo(x="yoyo")

运行结果

metaclass __new__: <class '__main__.DemoMetaClass'>, NewDemo, (<class 'object'>,), {'__module__': '__main__', '__qualname__': 'NewDemo', '__init__': <function NewDemo.__init__ at 0x000002039EDB9310>, '__new__': <function NewDemo.__new__ at 0x000002039EDB93A0>, 'name': 'zhangsan', 'age': <function DemoMetaClass.__new__.<locals>.<lambda> at 0x000002039EDB9430>}
metaclass 创建类初始化。。。。
---------------------------
class __new__:<class '__main__.NewDemo'>, (), {'x': 'yoyo'}
class __init__ : yoyo

标签:__,name,python,type,元类,print,new,metaclass,class
From: https://www.cnblogs.com/yoyoketang/p/17390482.html

相关文章

  • python用户输入
    第一讲:用户输入:定义了user_name和user_age以及user1_age3个变量,那么为什么不能让user1_age中的death_age-user_age呢?因为input中输入的都是字符串str,而death_age定义的是整数型int;无法用str去减int,所以要做个转换。那么我又去定义了个变量,user1_age,那么大家说可不可以去用去掉......
  • python基础学习-hashlib - 哈希函数模块
    hashlib-哈希函数模块参考地址:Python-Core-50-Courses/第20课:Python标准库初探.mdatmaster·jackfrued/Python-Core-50-Courses(github.com)待补充......哈希函数又称哈希算法或散列函数,是一种为已有的数据创建“数字指纹”(哈希摘要)的方法。哈希函数把数据压缩成摘要,对......
  • python基础学习-random
    参考地址:Python-Core-50-Courses/第20课:Python标准库初探.mdatmaster·jackfrued/Python-Core-50-Courses(github.com)待补充......random-随机数和随机抽样模块生成随机数、实现随机乱序和随机抽样,下面是常用函数的列表。getrandbits(k):返回具有k个随机比特位的整数......
  • python基础学习-os.path - 路径操作相关模块
    参考地址:Python-Core-50-Courses/第20课:Python标准库初探.mdatmaster·jackfrued/Python-Core-50-Courses(github.com)待补充......os.path模块封装了操作路径的工具函数,如果程序中需要对文件路径做拼接、拆分、获取以及获取文件的存在性和其他属性,这个模块将会非常有帮助......
  • Python打包exe,执行报player组件缺失“File "plyer\facades\notification.py", line
    之前的打包方式:pyinstaller--onefile--windowedpythonfilename.py执行exe报错:修改打包命令:pyinstaller--onefile--windowed--hidden-importplyer.platforms.win.notificationpythonfilename.py执行新的exe,正常弹窗,错误消失,win10toast组件实现类似功能,打包也......
  • python 内置常量
    1_debug_如果Python没有以-O选项启动,则此常量为真值-O表示移除assert语句以及任何以debug的值作为条件的代码注意O是大写的英文字母,小写会无法识别执行2Ellipsis等同于...属于ellipsis类型,就和None数据NoneType类型类似官方说这个东西主要用途是,自定......
  • 【2023最新】小白Anaconda+Python+Jupyter环境安装教程+kernel安装
    目录下载Anaconda安装包安装配置Anaconda环境测试是否安装成功为anaconda添加国内源创建环境JupyterNotebook启动Jupyter更改默认工作目录在jupyter中使用conda中安装的虚拟环境参考博客下载Anaconda安装包历史版本(最新版本可能安装不了旧版Python)https://repo.anaconda.com/......
  • python库之turtle库(1)
    Python的turtle库是一个用于绘制图形的库,它来自WallyFeurzeig,SeymourPapert于1967年在麻省理工学院MIT人工智能实验室开发的Logo编程语言。由于turtle绘图十分的直观而且十分受欢迎,所以turtle也逐渐的成为了Python的标准库之一。它很容易学习并且使用简单。安装turtle......
  • python基础学习-面向对象
     Python-Core-50-Courses/第17课:面向对象编程入门.mdatmaster·jackfrued/Python-Core-50-Courses(github.com)Python-Core-50-Courses/第18课:面向对象编程进阶.mdatmaster·jackfrued/Python-Core-50-Courses(github.com)......
  • python创建虚拟环境
    创建虚拟环境所用包virtualenv查看是否有虚拟环境virtualenv--version安装虚拟环境命令pipinstallvirtualenv创建虚拟环境(.venv是环境名字,可以随意取)virtualenv.venv激活虚拟环境cd.venv/Scripts目录下执行activate执行成功后即可此时即可安装第三方库或者运......