• 【python】超类简介__new__和__init__


    示例代码

    class Meta(type):
    
        def __new__(mcs, name, bases, attrs):
            print(locals())
            return super(Meta, mcs).__new__(mcs, name, bases, attrs)
    
        def __init__(cls, *args, **kwargs):
        	# 获得类原来的__init__方法
            _init = _init = getattr(cls, "__init__", None)
    
            def cls_init(self, *args, **kwargs):
                if _init:  # 如果子类定义了__init__方法,执行它
                    _init(self, *args, **kwargs)
                # 接下来做自己想做的操作
                print("inner init")
            print(locals())
            super(Meta, cls).__init__(*args, **kwargs)
            # 修改创建类的__init__方法
            setattr(cls, "__init__", cls_init)
    
    
    class AClass(metaclass=Meta):
        status = 1
    
        def __init__(self, name):
            print(f"a init and name is {name}")
    
    
    class AnotherClass(metaclass=Meta):
        status = 2
    
        def __init__(self, num):
    
            print(f"another init and num is {num}")
    
    
    a = AClass("小红")
    b = AClass("大红")
    c = AnotherClass(1)
    d = AnotherClass(100)
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41

    输出

    {'mcs': , 'name': 'AClass', 'bases': (), 'attrs': {'__module__': '__main__', '__qualname__': 'AClass', 'status': 1, '__init__': }, '__class__': }
    {'cls': , 'args': ('AClass', (), {'__module__': '__main__', '__qualname__': 'AClass', 'status': 1, '__init__': }), 'kwargs': {}, 'cls_init': .cls_init at 0x10895b520>, '_init': , '__class__': }
    {'mcs': , 'name': 'AnotherClass', 'bases': (), 'attrs': {'__module__': '__main__', '__qualname__': 'AnotherClass', 'status': 2, '__init__': }, '__class__': }
    {'cls': , 'args': ('AnotherClass', (), {'__module__': '__main__', '__qualname__': 'AnotherClass', 'status': 2, '__init__': }), 'kwargs': {}, 'cls_init': .cls_init at 0x10895b640>, '_init': , '__class__': }
    a init and name is 小红
    inner init
    a init and name is 大红
    inner init
    another init and num is 1
    inner init
    another init and num is 100
    inner init
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    超类写法

    当一个类继承了单独type后,这个类就成了可以创建其他类对象的类
    如:

    class Meta(type):
    	
        def __new__(mcs, name, bases, attrs):
            return super(Meta, mcs).__new__(mcs, name, bases, attrs)
    
        def __init__(cls, *args, **kwargs):
            super(Meta, cls).__init__(*args, **kwargs)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    __new__方法

    对于类来说,其创建也是会经过__new__和__init__的,和类实例一样。
    元类就可以在其中就行某些操作。元类的__new__接收四个参数,都是不具名参,分别是mcs:本元类类对象,name:类的名字、bases:类的父类们、attrs:类的属性。我们可以在这对类的属性进行一些操作,下面有一个通过此方法设计response类的实例

    __init__方法

    init方法是在执行完new之后,已经把类对象创建出后对其执行某些操作。
    此方法有也是四个参数,第一个参数是cls:__new__函数创建出的类,其余三个参数和__new__是相同的

    为创建出的类的__init__添加功能

    比如上边示例代码的元类的__init__方法

    new和init的区别

    类比类实例的创建过程,类的创建过程中的__new__函数的第一个参数实际上是元类(类实例的话是类),__init__函数的第一个参数是创建出的类本身(类实例的话是其本身)。

    通过元类设计一个response类

    import functools
    import json
    
    
    def gen_response_tuple(data=None, status_code=200, code=0, msg=""):
        """
        根据传入参数生成类似
        ({'data': {}, 'msg': 'invalid response', 'code': 10001}, 500)
        形式的元组用于接口返回。
        使用此方法以定义错误码、错误信息和http状态码
        """
        if data is None:
            data = {}
        return {"data": data, "code": code, "msg": msg, }, status_code
    
    
    class ResponseMeta(type):
        """
        此元类的目的是构造
        class HttpResponse(ResponseMeta):
            __module_prefix__ = 1
            Success = 200, 0, "ok"
            Failed = 200, 1, "failed"
        此类中虽然类属性是元组,但是通过元类构造成了一个只需要传递data的偏函数
        通过此类创造的类构造的实例可以用于接口返回
        如: return HttpResponse.Success(data={})
            这样接口返回 {'data': {}, 'msg': 'ok', 'code': 10000}, 200
        当需要抛出错误时,可以使用raise HttpResponse.Success.Exception(data={})
        这样在中间件中就可以捕获到错误值是错误提示的错误,避免raise的非友好提示
        """
        prefix_codes = dict()
    
        def __new__(mcs, name, bases, attrs):
            module_prefix = attrs.get("__module_prefix__", None)
            # 保证在使用错误类之前定义了错误码前缀,否则项目无法启动
            assert module_prefix is not None, f"class <{name}> must define __module_prefix__"
            # 保证错误码前缀不重复,否则项目无法启动
            assert module_prefix not in mcs.prefix_codes, f"module_prefix of class <{mcs.prefix_codes[module_prefix]}> " \
                                                          f"and  class <{name}> is conflicted"
            mcs.prefix_codes[module_prefix] = name
            # 将类属性(格式不为'__a__'的)都转化为偏函数,这个函数固定了status_code和code和msg,只需要填入data
            for k in filter(lambda x: not (x.startswith("__") and x.endswith("__")), attrs.keys()):
                assert isinstance(attrs[k], tuple) and len(attrs[k]) == 3, f"attribution <{k}> of class " \
                                                                           f"<{name}> is not a tuple like (200, 1, 'ok')"
    
                msg = attrs[k][2]
                code = module_prefix * 10000 + attrs[k][1]
                status_code = attrs[k][0]
                attrs[k] = functools.partial(
                    gen_response_tuple,
                    status_code=status_code,
                    code=code,
                    msg=msg,
                )
    
                def gen_exception(data_in=None, msg_in=msg, code_in=code, status_code_in=status_code):
                    return Exception(
                        json.dumps(dict(
                            msg=msg_in, code=code_in, status_code=status_code_in, data=data_in if data_in is not None else {}
                        ))
                    )
                # 添加Exception属性使此属性能被raise
                setattr(attrs[k], 'Exception', functools.partial(
                    gen_exception,
                    status_code_in=status_code,
                    code_in=code,
                    msg_in=msg,
                ))
            return super(ResponseMeta, mcs).__new__(mcs, name, bases, attrs)
    
    
    class BasicResponse(metaclass=ResponseMeta):
        __module_prefix__ = 1
        Success = 200, 0, 'success'
        NotFound = 200, 1, 'resource not found'
        UnknownError = 200, 9999, 'unknown error'
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
  • 相关阅读:
    Spring - FactoryBean扩展实战_MyBatis-Spring 启动过程源码解读
    使用rem、动态vh自适应移动端
    配置文件自动提示
    Linux进程概念详解
    搞懂 冒泡排序原理 锁和事务的原理(图文并茂)
    (ICRA 2020) Instance Segmentation of LiDAR Point Clouds
    二叉树习题
    10分钟搞定Mysql主从部署配置
    pwntools 安装
    【网络】个人博客网站搭建之Typecho(命令版)
  • 原文地址:https://blog.csdn.net/qq_42455809/article/details/127460263