zoukankan      html  css  js  c++  java
  • 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}
    
  • 相关阅读:
    Algorithm Gossip (48) 上三角、下三角、对称矩阵
    .Algorithm Gossip (47) 多维矩阵转一维矩阵
    Algorithm Gossip (46) 稀疏矩阵存储
    Algorithm Gossip (45) 费氏搜寻法
    Algorithm Gossip (44) 插补搜寻法
    Algorithm Gossip (43) 二分搜寻法
    Algorithm Gossip (42) 循序搜寻法(使用卫兵)
    Algorithm Gossip (41) 基数排序法
    Algorithm Gossip (40) 合并排序法
    AlgorithmGossip (39) 快速排序法 ( 三 )
  • 原文地址:https://www.cnblogs.com/KbMan/p/11290171.html
Copyright © 2011-2022 走看看