zoukankan      html  css  js  c++  java
  • 元类、class底层分析、对象生成控制

    太难了太难了,顶不住顶不住

    元类

    前方核能,这可能是我比较装逼的一讲,全部退后,我要开装了。

    首先我们要把什么是元类搞清楚。

    我们知道的,在python中,万物皆对象,其实在学到元类之前,我真的无法体会,我只知道这么一个概念,知道遇见了元类,我才体会到这个概念的意思。

    我们有类,然后通过实例化,来产生这个类的对象,是吧,那有没有想过,类是怎么来的,对没错,是class出来的,那有没有想过它里面发生了什么操作?

    其实,类他也是对象,他是元类的对象,又元类实例化出来的对象就是类!现在不要再把对对象的概念局限在只有通过类实例化出来的东西了,万物皆对象,是不是可以感受到那么一点点?

    那么元类的概念也清楚了,元类就是实例化类对象的类。那么元类是什么样子的呢?

    print(type(dict))
    print(type(list))
    print(type(str))
    print(type(object))
    
    print(type(type))
    

    <class 'type'>
    <class 'type'>
    <class 'type'>
    <class 'type'>
    <class 'type'>

    我们可以看到这些类的类型全都是 type类型的,就连 type 本身,都是 type 类。

    那么基本可以确定了, type ,就是元类。并且所有的类都是由它实例化出来的,然后被实例化出来的类才能实例化出一个个对象。

    那么既然元类也是类,第一个元类怎么来的?先有鸡还是先有鸡蛋?第一个元类是用C语言写的,相信小傻子们都明白了凡是遇到解释不清楚的东西,你都可以说是C写的。。。。。

    这个就不要去想了,他已经是最底层的东西了。

    class底层原理分析

    我们知道 class +类名,就会把类给构造出来了,通过上一节我们也知道了,其实并没有我们想的那么简单,实际上是元类实例化产生了类这个对象。

    实际上,它是经历了这么一个步骤。

    Person=type('Person',(object,),dict)
    

    type的返回值是一个类,Person来接收了这个返回值,于是就有了一个叫做‘Person’的类,赋值给了Person。你就可以用这个Person来实例化各种对象了。

    首先我们要讲一下这个各个参数的作用,第一个参数,很明显就是要创建的这个类的名字,第二个就是这个类要继承的父类,一定要写成元组或者列表的形式,因为你不一定只继承一个类,第三个才是关键,是一个字典,那么这个字典里面写的是什么呢?你回想一下你之前创建一个类的时候,是不是要写很多属性,很多方法在里面,这时候这些属性和方法,全都被挤到这个dict里面去了

    l={}
    exec('''
    school='oldboy'
    def __init__(self,name):
        self.name=name
    def score(self):
        print('分数是100')
    ''',{},l)
    

    exec方法,一共三个参数,第一个是字符串,第二个是全局变量存放的字典,第三个是局部变量的存放字典,我们要拿的是类里面的东西,所以我们需要的是第三个参数,然后全放进了 l 里面,这个exec的作用,他只是以字符串的形式把代码块放在了第一个参数的位置,然后执行这些代码,就会生成各种名称空间,有一个名称空间栈和名称空间堆,栈放变量名和方法名,堆放变量值和方法,打印出来为了方便观看,会直接以变量名:变量值的形式,但是方法不是,方法没办法打印出来,只能是方法名:方法地址,然后把这些名称空间的映射关系存在第三个参数 l 里面,可以说 l 里面存的就是映射关系。那么我们来打印一下这个 l。

    {'school': 'oldboy', '__init__': <function __init__ at 0x0000016746FD1EA0>, 'score': <function score at 0x00000167471BD048>}
    

    果然是以一个存放映射的字典,然后我们把这个字典扔进type的第三个参数里

    Person=type('Person',(object,),l)
    

    就变成了这样了,那么到这里,类就生成好了,我们就可以用这个Person类来实例化对象了。

    肯定有人会有这么一个问题,为什么type方三个参数会生成类,放一个参数却是返回类型。

    img

    看,他的源码就是这样的。我们用type这个元类实例化一个类的时候,就会调用type的init方法,里面具体是这样的。

    通过元类来控制类的产生

    我们知道的,元类就是type,已经写好了的,我们动不了,那我们怎么通过元类来控制类的产生呢,我们可以自己写,对自己写,然后把别的类的默认的元类改成自己写的元类就好了。

    自定义元类:来控制类的产生:可以控制类名,可以控制类的继承父类,控制类的名称空间

    自定义元类必须继承type,写一个类继承type ,这种类都叫元类

    class Mymeta(type):
        # def __init__(self,*args,**kwargs):
        def __init__(self,name,bases,dic):
            # self 就是Person类
            # print(name)
            # print(bases)
            # print(dic)
    

    这样我们就写好了一个自定义的元类了。

    来做个小练习

    练习一:加限制 控制类名必须以sb开头

    class Mymeta(type):
        # def __init__(self,*args,**kwargs):
        def __init__(self,name,bases,dic):
            if not name.startswith('sb'):
    			raise Exception('类名没有以sb开头')
    

    比如我们在实例化一个类

     class person(metaclass=Mymeta):
            name='nick'
    

    我们在类的括号里面加了一个metaclass=Mymeta,这就是指定了他的元类是我们自定义的那个类,而不是默认的type。这时候我们把name(也就是自身的类名),bases(也就是自身的父类,这里没有),和dic(自身的代码块)传进了自定义元类的init方法里,下面的操作你们就懂了。

    练习二:类必须加注释

    class Mymeta(type):
        # def __init__(self,*args,**kwargs):
        def __init__(self,name,bases,dic):
            if not self.__dict__['__doc__']:
                raise Exception('类里没有注释')
    

    这个就不多解释了,一样的道理

    通过元类控制类的调用过程

    这个和上面的有什么区别呢?

    区别就是这个才是细节

    控制类的调用过程,实际上在控制:对象的产生

    怎么实现?用__call__来实现

    比如说我们必须要新生成的类是以 qssb开头的,就可以在call里面写判断逻辑。

    class Mymeta(type):
        def __call__(self, *args, **kwargs):
            obj=object.__new__(self)
            obj.__init__(*args, **kwargs)
            # print(obj.__dict__)
            if not obj.__class__.__name__.startswith('qssb'):
                raise Exception('类名没有以qssb开头')
            return obj
    class Person(object,metaclass=Mymeta):
        school='oldboy'
        def __init__(self,name):
            self.name=name
        def score(self):
            print('分数是100')
    
    p=Person('nick')
    print(p.name)
    
    

    我不想解释这个代码了,我累了。

    因为你的实例化出的p,会调用Person(),所以就会执行他的元类的__call__方法,在这里面有调用了自身的init,懂了吧。首先我们看一下这个new,在他的括号里面写一个类的名称,就能够实例化出一个空的对象。然后就你懂得了

    有了元类之后的属性查找

    类的属性查找顺序:先从类本身中找--->mro继承关系去父类中找---->去自己定义的元类中找--->type中--->报错

    对象的属性查找顺序:先从对象自身找--->类中找--->mro继承关系去父类中找--->报错

    结束结束,腰痛

  • 相关阅读:
    Java Native Method
    SQL语句优化
    Ibatis的环境搭建以及遇到的问题解决
    Java 构建器
    SpringMVC自定义视图 Excel视图和PDF视图
    java 枚举的常见使用方法
    mysql 根据某些字段之和排序
    MFC The Screen Flickers When The Image Zoomed
    How To Debug Qmake Pro File
    Gcc And MakeFile Level1
  • 原文地址:https://www.cnblogs.com/chanyuli/p/11455247.html
Copyright © 2011-2022 走看看