• 深入理解Python中的元类---metaclass


    关于元类的一些说明

    1.  什么是元类?

      在我们不自定义元类的前提下,元类指的就是 type,而type实际上就是一个类。

    2. 元类的作用?

      元类是用于创建某个类的类,即说明了我要创建的类是通过哪个类创建的。

    3. 对创建类的重新认识

    大多是情况下创建类的方式:

    class Foo(object):
        pass

    然而也可以这样创建

    Foo = type("Foo",(object,),{})

    这两种创建类的效果等同。既然type是一个类,那么str和int是不是也是一个类?答案是肯定的。

    str和int既然都是一个类,那么这两个类是由哪个类创建的?

    由此可以看出,我们使用class创建一个类,使用str和int创建一个对象,其本质都是由 type这个类创建。既然我们知道了类都是由type这个类创建,那么是怎么通过类来创建类。对于Python而言,一切皆对象,即类也是一个对象。如果class只是用来创建一个类(即我们并不知道执行class实际上执行的是type),那么对于使用class关键子创建类的可以进行以下的分解操作:

    步骤一:

     Foo = type("Foo",(object,),{}) ,使用type这个类创建一个类名为Foo,继承object类的类,既然创建类的实例就会执行type类中的构造方法,而此时等号左边的Foo接收的是type类构造出来的实例对象。

    步骤二:

     class Foo(): pass ,将上述步骤生成的Foo变量进行传入,此时的Foo对于class来说是一个类,而该类其实就是type类的实例对象。

    既然我们知道了类其实都是由元类type进行创建的,那么我们就可以自定义一个元类类创建其他的类。

    自定义元类

    使用metaclass属性指定元类

    class MyType(type): # 自定义的元类需要继承元类type
        def __init__(self,*args,**kwargs):
            print('init')
            super(MyType,self).__init__(*args,**kwargs)
    
        def __call__(self, *args, **kwargs):
            print('call本质:调用类的__new__,再调用类的__init__')
            return super(MyType,self).__call__( *args, **kwargs)
    
    
    class Base(metaclass=MyType): # 使用自定义的元类创建Base类
        pass

    输出结果: init 。指定了metaclass属性,则会根据自定义元类的构造方法进行Base类的创建,因此打印出字符串“init”。也就是说,我们通过Mytype这个元类创建了Base类,更准确的说应该是Base类的实例对象。

    除了通过metaclass的属性设置元类,而通过继承的类也可以设置元类

    使用继承指定元类

    创建类的两种方式

    Base = MyType("Base",(object,),{})
    # 等同于
    class Base(object,metaclass=MyType): # 使用自定义的元类创建Base类
        pass

    注意:

      object类是由type类创建,同时在object类的源码中该类也继承了type类,说明了object类的metaclass是type,而MyType类的也继承了type类,也指明了该类的metaclass为type,因此不会发生metaclass冲突。

    两种继承方式

    class MyType(type): # 自定义的元类需要继承元类type
        def __init__(self,*args,**kwargs):
            print('init')
            super(MyType,self).__init__(*args,**kwargs)
    
        def __call__(self, *args, **kwargs):
            print('call本质:调用类的__new__,再调用类的__init__')
            return super(MyType,self).__call__( *args, **kwargs)
    
    
    Base = MyType("Base",(object,),{})
    
    
    #  第一种继承的方式
    class Foo(Base):
        pass
    
    
    # 第二种继承方式
    class Foo(MyType("Base",(object,),{})):
        pass

    输出结果: 

      init init 

    结果分析:

      首先通过元类MyType创建Base实例,而Foo又继承了该类,而该类的metaclass是MyType,因此间接的指名了Foo类的metaclass也为MyType,再由元类MyType创建该类的实例Foo。

    使用了继承的方式,派生类会自动继承父类的相关属性,如父类的metaclass是MyType,那么派生类的metaclass也是MyType,因此不需要在派生类中指定metaclass=MyType。

    下面的写法会报错:

    class MyType(type): # 自定义的元类需要继承元类type
        def __init__(self,*args,**kwargs):
            print('init')
            super(MyType,self).__init__(*args,**kwargs)
    
        def __call__(self, *args, **kwargs):
            print('call本质:调用类的__new__,再调用类的__init__')
            return super(MyType,self).__call__( *args, **kwargs)
    
    
    class XXX(type): # 自定义的元类需要继承元类type
        def __init__(self,*args,**kwargs):
            print('init_xxx')
            super(XXX,self).__init__(*args,**kwargs)
    
        def __call__(self, *args, **kwargs):
            print('call_xxx')
            return super(XXX,self).__call__( *args, **kwargs)
    
    
    Base = MyType("Base",(object,),{})
    
    
    class Foo(Base,metaclass=XXX):
        pass

    输出:

    "D:Program FilesPython36python.exe" D:/Demo/s8/flaskdemo/metaclass.py
    init
    Traceback (most recent call last):
      File "D:/Demo/s8/flaskdemo/metaclass.py", line 56, in <module>
        class Foo(Base,metaclass=XXX):
    TypeError: metaclass conflict: the metaclass of a derived class must be a (non-strict) subclass of the metaclasses of all its bases
    # 一个派生类的元类必须是它的所有基类(父类)的元类的一个子类 Process finished with exit code
    1

    结果很明显,metaclass冲突导致,产生的原因是继承的Base类的classmeta=MyType,但是我们却又指定Foo类的metaclass=XXX,因此造成冲突,将Foo类的metaclass=MyType即可,但既然继承了Base类,而Base类的metaclass就是MyType,再在Foo类中写metaclass就显的多余。

    对元类的常用形式

    class MyType(type):
        def __init__(self, *args, **kwargs):
            super(MyType, self).__init__(*args, **kwargs)
    
        def __call__(cls, *args, **kwargs):
            return super(MyType, cls).__call__(*args, **kwargs)
    
    # 将继承的类封装到一个函数中,在该函数中创建元类的实例
    def with_metaclass(base):
        return MyType('XX', (base,), {})
    
    
    class Foo(with_metaclass(object)):
        pass
     
  • 相关阅读:
    spring基于xml导入配置文件
    spring中bean的继承和依赖关系
    spring整合junit
    spring新注解说明
    Web微信开发工具无法输入中文?官方bug
    vue踩坑 导出new Vue.Store首字母要大写
    关于vue ui组件
    vue组件的生命周期
    Vue的指令以及组件化开发
    webpack的npm扩展使用
  • 原文地址:https://www.cnblogs.com/liuyinzhou/p/9697175.html
Copyright © 2020-2023  润新知