zoukankan      html  css  js  c++  java
  • 元类

    元类

    1. 什么是元类

    • 通过实例化产生类的类,称之为元类,元类实例化的结果就是class产生的类
    • 在Python中一切皆对象,那么用class关键字定义的类,本身也是对象
    • 产生该对象的类就称为元类,也可以简称为类的类

    2. 为什么要有元类

    • 元类是用来产生类的,元类和自定义元类,可以控制类的产生过程和对象的产生过程

    3. 创造名称空间---内置函数exec

    • exec的作用,是将字符串中的代码,提取出来执行,并产生名称空间
    • 语法exec(字符串,全局名称空间字典,局部名称空间字典)
    • 全局名称空间为{}目前不用考虑,只是占位
    • 执行字符串中的代码,产生的名字,都放到了局部名称空间字典中
    • 这一步,就模拟了类执行类体代码,产生名称空间的过程
    cmd='''
    x=1
    y=1
    print('--------------->')
    '''
    local_dic={}
    exec(cmd,{},local_dic)
    
    print(local_dic)
    ===============================
    --------------->
    {'x': 1, 'y': 1}
    
    cmd='''
    x=1
    def func(self):
        pass
    '''
    class_dic={}
    exec(cmd,{},class_dic)
    
    print(class_dic)
    ========================================================
    {'x': 1, 'func': <function func at 0x000001B4C14E1E18>}
    

    4. 创建类的原理过程

    • 创造类的3个要素:类名父类(基类)类的名称空间
    # 准备创造类的三要素:
    class_name='People'    # 类名
    class_bases=(object,)  # 父类(基类)
    class_dic={}           # 类的名称空间
    class_body='''
    country='China'
    def __init__(self,name,age):
        self.name=name
        self.age=age
    
    def eat(self):
        print('%s is eating' %self.name)
    '''
    # 创造名称空间
    exec(class_body,{},class_dic)
    
    # 三要素:
    print(class_name)
    print(class_bases)
    print(class_dic)
    ================================================================
    People
    (<class 'object'>,)
    {'country': 'China', '__init__': <function __init__ at 0x000002446E501E18>, 'eat': <function eat at 0x000002446E6DD158>}
    
    # 最后调用元类,即元类的实例化,产生了一个类
    People=type(class_name,class_bases,class_dic)
    print(People)
    ====================================================
    <class '__main__.People'>
    

    5. 创建类的方法一:使用class关键字创建

    • 如果说类也是对象,那么用class关键字去创建类的过程就是实例化的过程
    • 该实例化的目的是为了得到一个类,默认调用的就是元类
    class People:
        country='China'
        def __init__(self,name,age):
            self.name=name
            self.age=age
    
        def eat(self):
            print('%s is eating' %self.name)
    
    print(type(People))
    ===============================================
    #  默认的元类,是type,type()传入的参数是造类的关键
    <class 'type'>
    

    6. 创建类的方法二:自定义元类

    • 自定义就是为了控制类的创建过程
    class Mymeta(type): # 必须继承type类,才能自定义元类,否则就是一个普通的自定义类
        # 调用元类实例化类
        def __init__(self,class_name,class_base,class_dic): # 初始化类
            print(self)    # self是类名People
            print(class_name)
            print(class_base)
            print(class_dic)
            # 最好写上,用来重用父类type元类的其他功能
            super(Mymeta, self).__init__(class_name,class_base,class_dic)
    
    # class就是结合3个元素的3件事
    class People(object,metaclass=Mymeta):  # 基类 + metaclass指定元类名
        country='China'
        def __init__(self,name,age):
            self.name=name
            self.age=age
        def eat(self):
            print('%s is eating' %self.name)
    ====================================================================
    <class '__main__.People'>
    People
    (<class 'object'>,)
    {'__module__': '__main__', '__qualname__': 'People', 'country': 'China', '__init__': <function People.__init__ at 0x000001E56A1FD1E0>, 'eat': <function People.eat at 0x000001E56A1FD268>}
    

    7. 自定义元类的应用

    • 自定义元类,控制类的创建过程,类的产生过程其实就是元类的调用过程

    ① 要求创建的类必须加文档注释

    • 控制文档注释的是__doc__

    Alt text

    class Mymeta(type): 
        def __init__(self,class_name,class_base,class_dic):
            # 定制创建的类中必须要有文档注释
            if class_dic.get('__doc__') is None or len(class_dic.get('__doc__').strip()) == 0:
                raise  TypeError('类中必须有文档注释,并且注释不能为空')
            super(Mymeta, self).__init__(class_name,class_base,class_dic)
    
    class People(object,metaclass=Mymeta):
        '''这是一个注释'''  # 如果不写就会报错
        country='China'
        def __init__(self,name,age):
            self.name=name
            self.age=age
        def eat(self):
            print('%s is eating' %self.name)
    =======================================================================
    TypeError: 类中必须有文档注释,并且注释不能为空
    

    ② 要求创建的类名开头必须大写

    class Mymeta(type):
        def __init__(self,class_name,class_base,class_dic):
            if class_dic.get('__doc__') is None or len(class_dic.get('__doc__').strip()) == 0:
                raise  TypeError('类中必须有文档注释,并且注释不能为空')
            # 定制类名首字母必须大写
            if not class_name.istitle():
                raise TypeError('首字母必须大写')
            super(Mymeta, self).__init__(class_name,class_base,class_dic)
    
    class people(object,metaclass=Mymeta):
        '''注释文档'''
        country='China'
        def __init__(self,name,age):
            self.name=name
            self.age=age
        def eat(self):
            print('%s is eating' %self.name)
    =======================================================================
    TypeError: 首字母必须大写
    

    8. __call__

    • 将对象变为可调用的对象,就需要在该对象的类中定义__call__方法,该方法会在调用对象时,自动触发
    class Foo:
        pass
    
    obj=Foo()
    obj()
    =======================================
    # 默认对象不能调用
    TypeError: 'Foo' object is not callable
    
    class Foo:
        def __call__(self, *args, **kwargs):
            print(self)
            print(args)
            print(kwargs)
    
    obj=Foo()
    
    obj(1,2,3,x=1,y=2)
    =============================================
    <__main__.Foo object at 0x000001C8AB52C358>
    (1, 2, 3)
    {'x': 1, 'y': 2}
    
    • 通过自定义元类,可以控制类的调用过程,即控制类的实例化过程
    • 如果想让类可以调用,就需要在自定义的元类中添加__call__
    • 即当类被调用时,就自动触发了元类中的__call__
    • 以前是需要obj=People('xut',18)的形式,现在可以直接People()调用
    • 调类和调对象就是调__call__的方法,并且可以拿到这个方法的返回值,不写默认是None
    • 如何控制__call__,自定义元类的call
    class Mymeta(type):
        def __call__(self, *args, **kwargs):
            print(self)
            print(args)
            print(kwargs)
    
    class People(object,metaclass=Mymeta):
        country='China'
        def __init__(self,name,age):
            self.name=name
            self.age=age
    
        def eat(self):
            print('%s is eating' %self.name)
    
    # People()
    obj=People('xut',18)
    
    print(obj)
    =======================================================
    <class '__main__.People'>
    ('xut', 18)
    {}
    
    None # 调__call__的方法默认返回None
    
    • 说明:调对象(这里调的是类,People('xut',18))就是调__call__的方法,__call__返回值,赋值给一个变量(给obj
    • 如果类是对象,调类就是调元类中的__call__
    class Mymeta(type):
        def __call__(self, *args, **kwargs):
            print(self)
            print(args)
            print(kwargs)
            # 规定了返回值
            return 123
    
    class People(object,metaclass=Mymeta):
        country='China'
        def __init__(self,name,age):
            self.name=name
            self.age=age
    
        def eat(self):
            print('%s is eating' %self.name)
    
    
    obj=People('xut',18)
    print(obj)
    ===============================================
    <class '__main__.People'>
    ('xut', 18)
    {}
    123
    

    9. __new__

    • __new__用来创造空对象
    class Mymeta(type):
        def __call__(self, *args, **kwargs):
            # 1. 用__new__创造一个People的空对象
            self.__new__(self) # 这里的self是People
            # 2. 给这个空对象,初始化独有的属性
            print(args,kwargs)
    
    class People(object,metaclass=Mymeta):
        country='China'
        def __init__(self,name,age):
            self.name=name
            self.age=age
    
        def eat(self):
            print('%s is eating' %self.name)
    
    
    obj1=People('xut',age=18)
    obj2=People('xdw',age=18)
    print(obj1)
    print(obj2)
    ============================================================
    ('xut',) {'age': 18}
    ('xdw',) {'age': 18}
    None
    None
    
    
    People('xut',age=20)
    ============================================================
    ('xut',) {'age': 20}
    
    class Mymeta(type):
        def __call__(self, *args, **kwargs):
            # 1. 先造出一个People的空对象
            obj=self.__new__(self)
            # 2. 为该空对象初始化独有的属性
            self.__init__(obj,*args,**kwargs)
            # 3. 返回一个初始化好的对象
            return obj
    
    class People(object,metaclass=Mymeta):
        country='China'
        def __init__(self,name,age):
            self.name=name
            self.age=age
        def eat(self):
            print('%s is eating' %self.name)
    
    obj=People('xut',age=18)
    print(obj.__dict__)
    print(obj.name)
    obj.eat()
    ==============================================
    {'name': 'xut', 'age': 18}
    xut
    xut is eating
    
    class Mymeta(type):
        def __call__(self, *args, **kwargs):
            # 1. 先造出一个People的空对象
            obj=self.__new__(self)
            # 2. 为该空对象初始化独有的属性
            self.__init__(obj,*args,**kwargs)
            # 3. 返回一个初始化好的对象
            return obj
    
    class People(object,metaclass=Mymeta):
        country='China'
        def __init__(self,name,age):
            self.name=name
            self.age=age
        def eat(self):
            print('%s is eating' %self.name)
        def __new__(cls, *args, **kwargs):
            print(cls)
            obj=super().__new__(cls)
            return obj
    
    obj=People('xut',age=18)
    print(obj.__dict__)
    print(obj.name)
    obj.eat()
    =============================================
    <class '__main__.People'>
    {'name': 'xut', 'age': 18}
    xut
    xut is eating
    
  • 相关阅读:
    nginx upstream permission denied错误解决
    基于Mariadb 10.6.4在CentOS 7环境下配置Galera Cluster集群
    K8s 开始
    RTSP H264/HEVC 流 Wasm 播放
    Netty编码示例(RPC & WbeSocket & Tomcat)
    Netty异步任务调度与异步线程池
    Netty编解码器&TCP粘包拆包
    Netty核心模块组件
    Neety编码示例(群聊系统&⼼跳检测&WebSocket⻓连接)
    Netty高性能架构设计
  • 原文地址:https://www.cnblogs.com/itone/p/9248903.html
Copyright © 2011-2022 走看看