• python中的元类


    什么是元类

    所有的对象都是调用类(实例化)而得来的,调用类的过程叫做类的实例化。

    如果一切皆对象,那么类也是一个对象!既然对象都是通过调用类得到的,那么,调用类A得到了一个对象类B,那么类A就是元类!牛逼!

    class A:
        pass
    
    a = A()   #调用A得到一个对象a
    '''
    根据一切皆对象,类A也是一个对象,那么类A从哪来?
    A = 某个类()  某个类的实例化为A
    那么这个某个类,就叫元类!
    '''
    print(type(a))  # <class '__main__.A'>
    print(type(A))  # <class 'type'>
    print(type(type)) # <class 'type'>  元类type的元类为元类type
    #打印对象A的类,得出:A的元类为 type 类
    

    元类type——>实例化——>类A——>实例化——>对象a

    一个类有三大组成部分:

    • 类名 class_name :A
    • 基类们 class_bases :(object, ) 父类们
    • 类的名称空间 class_dict :执行类体代码得到的

    class 这个关键字帮我们做了什么?

    1. 拿到类名 class_name = A
    2. 拿到类的基类 class_bases = (object, )
    3. 执行类体代码,拿到类的名称空间 class_dict = {….}
    4. 调用元类得到类 type(class_name, class_bases, class_dict)

    创建类的三要素:类名,基类,类的名称空间

    用type创建类:type(class_name, class_bases, class_dict)

    自定义元类
    class MyType(type):  #自定义元类,继承type的类
    
        def __init__(self, class_name, class_bases, class_dict):  #类的三要素:类名,基类,名称空间
            super(MyType, self).__init__(class_name, class_bases, class_dict)
            print('self:', self)  #self: <class '__main__.People'>
            print(class_name)  # People
            print(class_bases)  # (<class 'object'>,)
            print(class_dict)  #{'__module__': '__main__', '__qualname__': 'People', '__init__': <function People.__init__ at 0x10c29fb00>, 'fun': <function People.fun at 0x10c358320>}
    
    
    
    class People(object, metaclass=MyType):  #等同于:People = type('People', class_bases, class_dict)
    
        def __init__(self, name, age):
            self.name = name
            self.age = age
    
        def fun(self):
            print('我是一个函数')
    
    # 类在定义的时候就执行了,所以People不用实例化,就能得出自定义元类中的结果
    
    元类的应用:

    元类中可以自定义类的产生过程。类的产生过程其实就是元类的调用过程

    # 例:实现创建类时必须有注释,类名必须大写,否则创建类会报错。
    class MyType(type):
        def __init__(self, class_name, class_bases, class_dict):
            super(MyType, self).__init__(class_name, class_bases, class_dict)
    
            if not class_name.istitle():
                raise TypeError('类名首字母需要大写')
    
            if not class_dict.get('__doc__') or not len(class_dict.get('__doc__').strip()):
                raise TypeError('类中必须要有注释,养成一个好习惯好吗弟弟?')
    
    
    class People(object, metaclass=MyType):
    
        def __init__(self, name, age):
            self.name = name
            self.age = age
    
        def fun(self):
            print('老子是个函数')
    
    
    __ call __

    要想让类的对象变成可调用的,必须要实现 __ call __ 方法。

    class TestCall:
    
        def __call__(self, *args, **kwargs):
            print('args:',args)
            print('kwargs:',kwargs)
    
    
    t = TestCall()
    t(1,x=2)
    # args: (1,)
    # kwargs: {'x': 2}
    
    #说明,类也是一个对象,所以在类的类中,也就是元类中肯定也实现了 __call__ 方法
    

    分析一波类的调用:

    类() 表示类的调用,要想让 类后面加个() 能用,这时候需要把类当做一个对象,这个对象的类中必然实现了 __ call __,我们知道,类的类时元类,所以元类中肯定实现了call方法,然后这个call返回了一个对象,元类的对象是类,类的对象就是 元类中 call 返回的对象。

    __ new __:

    我们知道,类在实例化的时候,会第一个调用执行类里面 __ init __ 里面的代码。但是!转折来了,类在实例化的时候,第一个调用的并不是 __ init __ 的方法,而是 __ new __,这个方法会返回一个空对象,然后再调用 init 初始化,将类实例化时后面跟的参数添加进 这个空对象 的名称空间中。

    __ new __ 后面跟的参数跟 __ init __ 一样。

    __ init __ 是在类实例(对象)创建之后调用的,而 __ new __ 这个方法,正是产生类实例(对象)的方法

    注意:__ new __ 只能用于新式类(从object继承的类)

    class People():
    
        def __new__(cls, *args, **kwargs):
            print('new_args', args)  # ('egon', 19) 这个是类实例化的时候后面跟的参数
            print('new_kwargs', kwargs)
            return cls.__new__(cls)  #自己找自己的 __ new __,会无限循环。
    
        def __init__(self, name, age):
            print('name', name)
            print('age', age)
    
    
    p = People('egon', 19)
    
    #说明:这个类实例化时,第一步会先调用 __ new __ 方法 ,生成一个空对象,在People类中有一个 new ,这时候创建空对象时,会一直调用自己的 __ new __ 方法,陷入无限循环中。
    
    利用元类自定义类的实例化

    自定义类的实例化过程,本质上就是重写元类的 __ call __ 方法。因为类实例化,就是在调用元类。

    class MyType(type):
        def __call__(self, *args, **kwargs):
    
            print('self:', self) # self: <class '__main__.People'>
            print('**args:', args) # **args: ('KbMan', 12)
            print('**kwargs:', kwargs) # **kwargs: {}
    
            obj = self.__new__(self) # 1、先创建一个空对象,这个self是类 People
            self.__init__(obj, *args, **kwargs) # 2、初始化空对象的属性
            return obj # 3、返回初始化之后的对象
    
    
    class People(object, metaclass=MyType):
        def __init__(self, name, age):
            self.name = name
            self.age = age
    
    
    p = People('KbMan', 12)  # 类实例化时,因为类的实例化就是调用元类的过程,所以会调用元类的 __call__方法
    print(p.__dict__) # {'name': 'KbMan', 'age': 12}
    

    类的调用即类的实例化,就是元类的调用过程,可以通过自定义元类 MyType 的 __ call __ 控制调用过程。

    类实例化分析:

    1、类实例化实际上就是调用元类的过程,就是调用元类中 __ call __ 方法的过程

    2、利用 __ new __ 生成一个People的空对象

    3、为该对象调用 __ init __ 初始化属性值

    4、返回该对象

    自定义元类后的继承顺序
    class MyType(type):
    
        s = 'MyType元类'
    
    
    class B:
        # s = 'B类'
        def __init__(self):
            super().__init__()
    
    class A(B):
        # s = 'A类'
        def __init__(self):
            super().__init__()
    
    class People(A, metaclass=MyType):
        # s = 'People类'
    
        def __init__(self):
            super().__init__()
    
    print(People.s)  # 查找顺序为 People类 ——> A类 ——> B类 ——> MyType类
    

    查找顺序:

    1. 先对象层:People->A->B->object
    2. 然后元类层:MyType->type
    自定义元类后 __ new __ 的查找顺序
    class MyType(type):
    
        s = 'MyType元类'
        def __call__(self, *args, **kwargs):
    
            obj = self.__new__(self)
            print(self.__new__ is object.__new__)
    
    
    class B:
        # s = 'B类'
        def __init__(self):
            super().__init__()
    
        # def __new__(cls, *args, **kwargs):
        #     print('B类的__new__')
    
    
    class A(B):
        # s = 'A类'
        def __init__(self):
            super().__init__()
    
        # def __new__(cls, *args, **kwargs):
        #     print('A类的__new__')
    
    
    class People(A, metaclass=MyType):
        # s = 'People类'
    
        def __init__(self):
            super().__init__()
    
        # def __new__(cls, *args, **kwargs):
        #     print('People的__new__')
    
    
    People() # __new__ 的 查找顺序为 People类 ——> A类 ——> B类 ——>object#
    #注意:object类中自带 __new__,所以查到object类 肯定会结束
    
    利用元类修改类的属性为私有属性(隐藏属性)
    # 要修改的属性为类实例化出的对象的属性。
    
    class MyType(type):
        def __init__(self, class_name, class_bases, class_dict):
            super().__init__(class_name, class_bases, class_dict)
    
        # 在类实例化时修改对象属性为隐藏属性
        def __call__(self, *args, **kwargs):
            # 1、创建一个空对象
            obj = self.__new__(self)
            # 2、调用 __init__ 初始化对象属性
            self.__init__(obj, *args, **kwargs)
            # 3、修改对象的__dict__
            obj.__dict__ = { '_{0}__{1}'.format(self.__name__, k) : v  for k, v in obj.__dict__.items()}
            return obj
    
    
    class People(object, metaclass=MyType):
        def __init__(self, name, age):
            self.name = name
            self.age = age
    
    
    p = People('KbMan',123)
    print(p.__dict__)  # {'_People__name': 'KbMan', '_People__age': 123}
    
  • 相关阅读:
    VS插件哪家强?CodeRush v20.2帮你忙
    WinForms界面开发工具DevExpress WinForms v20.2亮点——全新Sankey Diagram控件震撼发布
    java中将信息写入excel
    java中使用IO流将以文件中的内容去取到指定的文件中
    java中使用IO流复制文件
    采购订单写入sap失败后,抛出自定义异常,回滚数据库
    java中将文件夹里面的文件复制到指定的文件夹(java IO)
    JAVA中IO流详解
    获取员工合同信息列表 定时任务
    Java连接MySQL数据库——含步骤和代码
  • 原文地址:https://www.cnblogs.com/KbMan/p/11290171.html
Copyright © 2020-2023  润新知