QQ登录

只需一步,快速开始

开启左侧

Python学习第七十三天—类和对象22-元类

[复制链接]
15271953841 发表于 2024-4-18 08:32:43 | 显示全部楼层 |阅读模式

马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。

您需要 登录 才可以下载或查看,没有账号?注册

x
元类

元类(metaclass)是99%用户无需关注的终极魔法,如果你还在犹豫是否需要使用元类,那么你肯定是不需要的,因为真正是需要使用元类的人是不会去纠结这个问题的——Tim Peters,他写了一段名言警句:
Python 3.12.1 (tags/v3.12.1:2305ca5, Dec  7 2023, 22:03:25) [MSC v.1937 64 bit (AMD64)] on win32
Type "help", "copyright", "credits" or "license()" for more information.
>>>import this
The Zen of Python, by Tim Peters

Beautiful is better than ugly.
Explicit is better than implicit.
Simple is better than complex.
Complex is better than complicated.
Flat is better than nested.
Sparse is better than dense.
Readability counts.
Special cases aren't special enough to break the rules.
Although practicality beats purity.
Errors should never pass silently.
Unless explicitly silenced.
In the face of ambiguity, refuse the temptation to guess.
There should be one-- and preferably only one --obvious way to do it.
Although that way may not be obvious at first unless you're Dutch.
Now is better than never.
Although never is often better than *right* now.
If the implementation is hard to explain, it's a bad idea.
If the implementation is easy to explain, it may be a good idea.
Namespaces are one honking great idea -- let's do more of those!
什么是类,类就是用来创建对象的母版,上节课我们知道,类原来不是最大的大佬,它的背后还有一个幕后黑手——type(),类是由type创造而来的。
元类:通俗来讲,元类就是创造类的母版上升了一个级别,为什么type类能用类创造类,因为type本身就是一个元类,使用元类来构造类,这么说就合理了,类之间是存在继承关系的,元类也一样,所有的元类都是继承自type,因为type只能是一行流的写法,不好发挥,比较搞脑袋,弄不好就会陷入思维漩涡,对不对,过瘾就过瘾,就是不太友好。
我们要来学习自己定义一个元类,想要创建一个元类,让它继承自type
>>>class MetaC(type):              (创建一个最简单的元类,继承自type元类,type是所有元类的父类)
    pass

>>>class C(metaclass = MetaC):         (如果想要通过元类来创造类,那么需要在类的小括号中,使用metaclass这个关键字引入刚刚创建好的元类MetaC,采用metaclass=MetaC)
    pass

>>>c = C()           (首先来验证一下,实例化一个对象)
>>>type(c)              (看看type(小c)得到的是什么,得到的是__main__.C,是来自于C这个类的)
<class '__main__.C'>
>>>type(C)                      (再看大C这个类来自于什么?它是来自于__main__.MetaC,就是刚刚创建的这个元类,如果我们没有通过这个元类来指定的话,那么得到的就是来自于type,现在相当于元类就是类和type之间架起了一道桥梁)
<class '__main__.MetaC'>
>>>type(MetaC)            (如果这里传入的MetaC,那么它得到就是type了)
<class 'type'>

在学习魔法方法的时候,对象在被创建的时候,就会去调用__init__()方法,所以__init__()我们叫它构造函数,但__init__()并非是实例化对象调用的第一个魔法方法,第一个应该是__new__()魔法方法,那么现在来同时定义元类和类里面的__new__()和__init__()方法,然后来看看它们执行逻辑到底是怎么样的。(参考第59课时)
>>>class MetaC(type):
    def __new__(mcls,name,bases,attrs):        (__new__是返回实例化对象,需要return)
        print("__new__() in MetaC~")
        return type.__new__(mcls,name,bases,attrs)      (__new__的话是由它的父类来实现的,那么元类的父类就是type)
    def __init__(cls,name,bases,attrs):           (__init__是传入参数,不需要return)
        print("__init__() in MetaC~")
        type.__init__(cls,name,bases,attrs)

        
>>>class C(metaclass = MetaC):
    def __new__(cls):                      (__new__是返回实例化对象,需要return)
        print("__new__() in C~")
        return super().__new__(cls)
    def __init__(self):                       (__init__是传入参数,不需要return)
        print("__init__() in C~")

        
__new__() in MetaC~
__init__() in MetaC~                  (类C定义刚完成,回车的时候一瞬间就已经被调用了,__new__() in MetaC和__init__() in MetaC就已经被调用了,非常快)
>>>c = C()
__new__() in C~
__init__() in C~                  (现在实例化对象,才发现类里面的__new__() in C和__init__() in C才被触发。)
两给__new__方法有直接关联的吗?这个class C里面的super().__new__调用的其元类MetaC里面的__new__()方法吗???其实不是,元类MetaC里面的__new__方法是类C完成的那一刻被触发的,而类C的__new__()方法是类C实例化对象的那一刻才触发,两者触发的时间点是不一样的,它的参数也不一样,那么类C的super().__new__调用的是谁的__new__,是object的,当一个类没有指定父类的时候,就会找到object,因为object默认是所有类的爹,父类和子类是同一个类别的物质,而元类则是比类更高一个级别,普通的类比作是人,元类就是神,type就是众神之神。

>>>class MetaC(type):
    def __new__(mcls,name,bases,attrs):
        print("__new__() in MetaC~")
        print(f"mcls={mcls},name={name},bases={bases},attrs={attrs}")     (元类的参数是什么,让它打印出来)
        return type.__new__(mcls,name,bases,attrs)
    def __init__(cls,name,bases,attrs):
        print("__init__() in MetaC~")
        print(f"cls={cls},name={name},bases={bases},attrs={attrs}")
        type.__init__(cls,name,bases,attrs)

        
>>>class C(metaclass = MetaC):
    def __new__(cls):
        print("__new__() in C~")
        return super().__new__(cls)
    def __init__(self):
        print("__init__ in C~")

        
__new__() in MetaC~
mcls=<class '__main__.MetaC'>,name(类名)=C,bases(父类)=(),attrs(对象的这个类里面的属性和方法)={'__module__': '__main__', '__qualname__': 'C', '__new__': <function C.__new__ at 0x000001B62C1CF100>, '__init__': <function C.__init__ at 0x000001B62C1CF1A0>, '__classcell__': <cell at 0x000001B62C1C8B20: empty>}
__init__() in MetaC~
cls=<class '__main__.C'>,name=C,bases=(),attrs={'__module__': '__main__', '__qualname__': 'C', '__new__': <function C.__new__ at 0x000001B62C1CF100>, '__init__': <function C.__init__ at 0x000001B62C1CF1A0>, '__classcell__': <cell at 0x000001B62C1C8B20: MetaC object at 0x000001B62BE22AA0>}

二、上面演示(demo)过了,还有一个重要的,用的比较多的__call__方法(参考第65课时),在类里面__call__方法就是拦截对象被当作函数调用时的操作,那么现在,如果我们把__call__方法定义到元类中,你觉得它拦截的是什么操作,它应该拦截类实例化对象的操作,升高了一个等级。
>>>class MetaC(type):
    def __call__(cls,*args,**kwargs):
        print("__call__() in MetaC~")

        
>>>class C(metaclass = MetaC):
    pass

>>>c = C()
__call__() in MetaC~
当我们在实例化对象(类C)时,发现这个__call__()in MetaC就被调用了。
我们怎么使用类去定义对象的?
现在上升一个级别,我们怎么使用元类定义一个类的?
正是因为元类高高在上,处于神级,所以它的威力是非常强大的,但是绝大多数都是通过我们今天介绍的三个魔法方法来实现的,那么在实际开发中,元类又能起到什么关键性的作用呢?下一节课,我们将学习元类在实际开发中玩法。

客服热线
400-1234-888 周一至周日:09:00 - 21:00
公司地址:襄阳市樊城区长虹路现代城5号楼188

创客帮MAKER.BAND青少年创客创意社区是一个融教育、科技、体育资讯为一体的综合服务平台,专注于教育创新、专注于科技体育、专注于教育资讯。

Powered by Discuz! X3.4 © 2001-2013 Comsenz Inc.

快速回复 返回顶部 返回列表