• python @ 装饰器(修饰器,语法糖)使用与不使用对比总结记录


    由于看python代码里面函数上的@ 不爽很久了,为了避免自己又忘记了这里来记录下。
    简单总结:
    @ 的作用就是在使用 @ 下面的函数(如下图的cs2)的时候,会在该函数执行前将该函数作为参数扔到@后跟着的处理函数先行处理(或预备处理)些东西,如下图的 time_cs 就是本文举的封装一个计时函数的装饰器。

    阅读下面代码对比使用语法糖的使用,即可了解。
    本篇博客自参考b站大佬up的视频
    在这里插入图片描述
    在这里插入图片描述

    一.预备的python常识

    1.python中函数加括号与不加返回的区别

    区分a()和a如下
    一个是: 函数a在电脑中的十六进制地址
    一个是: 函数a返回的值

    def a():
        return 1
    
    print(a) # 函数a在电脑中的十六进制地址
    print(a()) # 函数a返回的值
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    输出

    <function a at 0x000001E37E1C6DC8>
    1
    
    • 1
    • 2

    2.在python中函数可以作为参数直接传入

    
    def a():
        return "a的返回"
    
    def b():
        return "b的返回"
    
    def out(action):
        """传入函数对象"""
        print(action())
    
    out(a)
    out(b)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    输出

    a的返回
    b的返回
    
    
    • 1
    • 2
    • 3

    二. 计时函数的例子说明

    1.1 不使用装饰器的写法简单-0(无输入输出)

    即检测的目标函数没有输入值也没有返回值的情况

    import time
    
    def time_cs(func):
        """
    
        :param func: 传入的你需要计时的函数
        :return: 无
        """
    
        print("开始计时,打下时间戳")
    
        start = time.perf_counter()
    
        func()
    
        end = time.perf_counter()
    
        print("完成计时,消耗时间" + str(end-start))
        print("\n")
    
    def cs():
        print("\n")
        print("*"*8 + "fun" + "*"*8)
        print("只是打印当前信息的测试函数")
        print("*" * 20)
        print("\n")
        time.sleep(0.25)
    
    time_cs(cs)
    
    • 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

    输出

    开始计时,打下时间戳
    
    
    ********fun********
    只是打印当前信息的测试函数
    ********************
    
    
    完成计时,消耗时间0.2602558
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    1.2 不使用装饰器的写法简单-1(只有输出)

    即目标函数拥有返回值的情况
    这里比较麻烦需要在time_cs 函数中将返回值用一个变量存储,使得你需要预先知道目标函数返回值的个数

    import time
    
    def time_cs(func):
        """
    
        :param func: 传入的你需要计时的函数
        :return: func函数的输出
        """
    
        print("开始计时,打下时间戳")
    
        start = time.perf_counter()
    
        num = func()
    
        end = time.perf_counter()
    
        print("完成计时,消耗时间" + str(end-start))
        print("\n")
        return num
    
    def cs2():
        print("\n")
        print("*"*8 + "fun2" + "*"*8)
        print("打印当前信息的测试函数,并返回一个整形的10")
        print("*" * 20)
        print("\n")
        time.sleep(0.25)
        return 10
    
    def cs3(num):
        print("cs3函数得到了cs2函数的输出:" + str(num))
    
    
    
    
    cs3(time_cs(cs2))
    
    • 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

    输出

    开始计时,打下时间戳
    
    
    ********fun2********
    打印当前信息的测试函数,并返回一个整形的10
    ********************
    
    
    完成计时,消耗时间0.257934
    
    
    cs3函数得到了cs2函数的输出:10
    
    进程已结束,退出代码 0
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    1.3.不使用装饰器的写法简单-2(有输入有输出)

    即目标函数拥有有输入有输出返回值的情况,

    1.3.1失败版

    首先展示一个能运行但是,无法成功获得目标函数运行时间的写法,这里time_cs输出内部输出由result = func()改成了 result = func

    import time
    
    
    def time_cs(func):
        """
        :param func: 传入的你需要计时的函数
        :return: func函数的输出
        """
    
    
        print("开始计时,打下时间戳")
    
        start = time.perf_counter()
    
        result = func # 注意这里!!不是 func()!!
        print("传入的函数的id: " + str(id(func)))
    
        end = time.perf_counter()
    
        print("完成计时,消耗时间" + str(end - start))
        print("\n")
        return result
    
    
    
    
    
    def cs2(num):
        print("\n")
        print("*"*8 + "fun2" + "*"*8)
        print("打印当前信息的测试函数,并返回一个整形的" + str(num))
        print("*" * 20)
        print("\n")
        time.sleep(0.25)
        return num
    
    def cs3(num):
        print("cs3函数得到了cs2函数的输出:" + str(num))
    
    
    num = time_cs(cs2(5))
    cs3(num)
    
    
    
    • 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

    输出如下,可以看到该写法下的消耗时间统计是错误的
    原因:传入的时候就是传入了一个 cs2(5) 即一个完成了执行了time.sleep(0.25) 之后的程序进入,因此下面输出的9.900000000007125e-06 代表的时间实际就可以理解成输入的func函数运行时间为0时,time_cs函数自身的运行时间

    ********fun2********
    打印当前信息的测试函数,并返回一个整形的5
    ********************
    
    
    开始计时,打下时间戳
    传入的函数的id: 140720369607200
    完成计时,消耗时间9.900000000007125e-06
    
    
    cs3函数得到了cs2函数的输出:5
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    1.3.1成功版
    import time
    
    
    def time_cs(func,num):
        """
        :param func: 传入的你需要计时的函数
        :return: func函数的输出
        """
    
    
        print("开始计时,打下时间戳")
    
        start = time.perf_counter()
    
        result = func(num)
        print("传入的函数的id: " + str(id(func)))
    
        end = time.perf_counter()
    
        print("完成计时,消耗时间" + str(end - start))
        print("\n")
        return result
    
    
    
    
    
    def cs2(num):
        print("\n")
        print("*"*8 + "fun2" + "*"*8)
        print("打印当前信息的测试函数,并返回一个整形的" + str(num))
        print("*" * 20)
        print("\n")
        time.sleep(0.25)
        return num
    
    def cs3(num):
        print("cs3函数得到了cs2函数的输出:" + str(num))
    
    
    num = time_cs(cs2,5)
    cs3(num)
    
    • 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

    输出

    开始计时,打下时间戳
    
    
    ********fun2********
    打印当前信息的测试函数,并返回一个整形的5
    ********************
    
    
    传入的函数的id: 2832141314952
    完成计时,消耗时间0.2586813
    
    
    cs3函数得到了cs2函数的输出:5
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    1.3.不使用装饰器的写法简单-4(引入返回函数)

    (目标函数拥有输入有输出)
    这里将time_cs这个统计输入函数运行时间的功能函数,改造成输入和输出都是函数 的功能函数,这里是最接下面近装饰器写法的,建议与下面的2.使用装饰器的写法对比看

    import time
    import functools
    
    def time_cs(func):
        """
    
        :param func: 传入的你需要计时的函数
        :return: func函数的输出
        """
        def my_wrapper(*args, **kwargs):
            """再创建一个装饰器函数"""
            print("开始计时,打下时间戳")
    
            start = time.perf_counter()
    
            result = func(*args, **kwargs)
            print("传入的函数的id: " + str(id(func)))
    
            end = time.perf_counter()
    
            print("完成计时,消耗时间" + str(end - start))
            print("\n")
            return result
    
        return my_wrapper
    
    def cs2(num):
        print("\n")
        print("*"*8 + "fun2" + "*"*8)
        print("打印当前信息的测试函数,并返回一个整形的" + str(num))
        print("*" * 20)
        print("\n")
        time.sleep(0.25)
        return num
    
    def cs3(num):
        print("cs3函数得到了cs2函数的输出:" + str(num))
    
    
    cs2_2 = time_cs(cs2)
    cs3(cs2_2(5))
    print("传出的函数的id: " + str(id(cs2_2)))
    print("传出的函数的名字: " + str(cs2_2.__name__)) # 不加 @functools.wraps(func) 就是输出my_wrapper
    
    
    • 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

    输出

    开始计时,打下时间戳
    
    
    ********fun2********
    打印当前信息的测试函数,并返回一个整形的5
    ********************
    
    
    传入的函数的id: 1392449547432
    完成计时,消耗时间0.2594437
    
    
    cs3函数得到了cs2函数的输出:5
    传出的函数的id: 1392449545992
    传出的函数的名字: my_wrapper
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    特别的加上 @functools.wraps(func) 以保证传输的函数名字不变

    如下

    import time
    import functools
    
    def time_cs(func):
        """
    
        :param func: 传入的你需要计时的函数
        :return: func函数的输出
        """
        @functools.wraps(func) # 这里的作用是使得返回的 my_wrapper 的 .__name__ 名字
                                # 和传入的 func.__name__  的相同,如下print(cs2_2.__name__)
        def my_wrapper(*args, **kwargs):
            """再创建一个装饰器函数"""
            print("开始计时,打下时间戳")
    
            start = time.perf_counter()
    
            result = func(*args, **kwargs)
            print("传入的函数的id: " + str(id(func)))
    
            end = time.perf_counter()
    
            print("完成计时,消耗时间" + str(end - start))
            print("\n")
            return result
    
    
    
        return my_wrapper
    
    
    def cs2(num):
        print("\n")
        print("*"*8 + "fun2" + "*"*8)
        print("打印当前信息的测试函数,并返回一个整形的" + str(num))
        print("*" * 20)
        print("\n")
        time.sleep(0.25)
        return num
    
    def cs3(num):
        print("cs3函数得到了cs2函数的输出:" + str(num))
    
    
    cs2_2 = time_cs(cs2)
    cs3(cs2_2(5))
    print("传出的函数的id: " + str(id(cs2_2)))
    print("传出的函数的名字: " + str(cs2_2.__name__)) # 不加 @functools.wraps(func) 就是输出my_wrapper
    
    • 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

    输出

    开始计时,打下时间戳
    
    
    ********fun2********
    打印当前信息的测试函数,并返回一个整形的5
    ********************
    
    
    传入的函数的id: 2366053213960
    完成计时,消耗时间0.2593284
    
    
    cs3函数得到了cs2函数的输出:5
    传出的函数的id: 2366053215688
    传出的函数的名字: cs2
    
    进程已结束,退出代码 0
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    2.使用装饰器的写法(有输入输出)

    能够达到以上的效果最接近上面1.3,且使用起来更加的方便

    import time
    import functools
    
    def time_cs(func):
        """
    
        :param func: 传入的你需要计时的函数
        :return: func函数的输出
        """
        @functools.wraps(func) # 这里的作用是使得返回的 my_wrapper 的 .__name__ 名字
                                # 和传入的 func.__name__  的相同,如下print(cs2_2.__name__)
        def my_wrapper(*args, **kwargs):
            """再创建一个装饰器函数"""
            print("开始计时,打下时间戳")
    
            start = time.perf_counter()
    
            result = func(*args, **kwargs)
            print("传入的函数的id: " + str(id(func)))
    
            end = time.perf_counter()
    
            print("完成计时,消耗时间" + str(end - start))
            print("\n")
            return result
    
        return my_wrapper
    
    @time_cs
    def cs2(num):
        print("\n")
        print("*"*8 + "fun2" + "*"*8)
        print("打印当前信息的测试函数,并返回一个整形的" + str(num))
        print("*" * 20)
        print("\n")
        time.sleep(0.25)
        return num
    
    def cs3(num):
        print("cs3函数得到了cs2函数的输出:" + str(num))
    
    
    cs3(cs2(5))
    
    print("传出的函数的名字: " + str(cs2.__name__)) # 不加 @functools.wraps(func) 就是输出my_wrapper
    
    
    
    • 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

    输出

    开始计时,打下时间戳
    
    
    ********fun2********
    打印当前信息的测试函数,并返回一个整形的5
    ********************
    
    
    传入的函数的id: 2966589466664
    完成计时,消耗时间0.26083870000000003
    
    
    cs3函数得到了cs2函数的输出:5
    传出的函数的名字: cs2
    
    进程已结束,退出代码 0
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    三.装饰器其他使用应用记录

    1.自动重启调用

    这里以一个简单的socket客户端与服务端举例,模拟服务端故障没有打开,而客户端连接失败时,在装饰器作用下客户端自动重连
    以下代码参考视频27.58分说明

    (1)简单socket客户端(带@重连)
    #!/usr/bin/env python
    # -*- coding:utf-8 -*-
    # -*-author:Chenhui
    
    from socket import *
    import time
    import functools
    
    def retry_and_show(exception: Exception, tries: int = 10, delay: int = 1, backoff: int = 2):
        """
    
        :param exception: 错误;类型
        :param tries: 重试次数
        :param delay: 间隔重试时间间隔 秒
        :param backoff: 间隔延时持续增加步长
        :return:
        """
        def decorator_retry(func):
    
            @functools.wraps(func)
            def run_with_retry_police(*args,**kwargs):
                _tries, _delay = tries,delay
                total_time = 1
                while _tries > 1:
                    try:
                        return func(*args,**kwargs)
                    except exception:
    
                        time.sleep(_delay)
                        total_time = total_time + _delay
                        print("程序错误" + str(_delay) + "秒后自动重新尝试,剩余尝试次数:"
                              + str(_tries) + "次, 增加等待时间为:" + str(_delay)
                              + "秒, 已尝试用时:" + str(total_time) + "秒")
    
    
    
                        _tries -= 1
                        _delay += backoff
                return func(* args, ** kwargs)
    
            return run_with_retry_police
    
        return decorator_retry
    
    @retry_and_show(ConnectionRefusedError) # ConnectionRefusedError: [WinError 10061] 由于目标计算机积极拒绝,无法连接。
    def connect_server():
        """自定义连接服务端,并启动一个循环开启处理逻辑"""
    
        IP = '127.0.0.1'
        SERVER_PORT = 6666
        BUFLEN = 512
    
        # 实例化一个sokcet对象,指明协议
        dataSocket = socket(AF_INET, SOCK_STREAM)
    
        # 连接服务端socket
        dataSocket.connect((IP, SERVER_PORT))
    
        while True:
            # 从终端读入用户输入的字符串
            toSend = input('成功连接至socket服务端,请输入发送数据\n>>> ')
            if toSend == 'exit':
                break
            dataSocket.send(toSend.encode())
            recved = dataSocket.recv(BUFLEN)
            # 如果返回空bytes,表示对方关闭了连接
            if not recved:
                break
            # 打印读取的信息
            print(recved.decode())
            #dataSocket.close()
    
    if __name__ == '__main__':
        connect_server()
    
    
    
    • 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

    输出

    程序错误1秒后自动重新尝试,剩余尝试次数:10次, 增加等待时间为:1秒, 已尝试用时:2秒
    程序错误3秒后自动重新尝试,剩余尝试次数:9次, 增加等待时间为:3秒, 已尝试用时:5秒
    程序错误5秒后自动重新尝试,剩余尝试次数:8次, 增加等待时间为:5秒, 已尝试用时:10秒
    程序错误7秒后自动重新尝试,剩余尝试次数:7次, 增加等待时间为:7秒, 已尝试用时:17秒
    程序错误9秒后自动重新尝试,剩余尝试次数:6次, 增加等待时间为:9秒, 已尝试用时:26秒
    程序错误11秒后自动重新尝试,剩余尝试次数:5次, 增加等待时间为:11秒, 已尝试用时:37秒
    程序错误13秒后自动重新尝试,剩余尝试次数:4次, 增加等待时间为:13秒, 已尝试用时:50秒
    程序错误15秒后自动重新尝试,剩余尝试次数:3次, 增加等待时间为:15秒, 已尝试用时:65秒
    程序错误17秒后自动重新尝试,剩余尝试次数:2次, 增加等待时间为:17秒, 已尝试用时:82秒
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    (2)简单服务端
    #!/usr/bin/env python
    # -*- coding:utf-8 -*-
    # -*-author:Chenhui
    
    """这个服务器只做一件事,就是将接受到的消息原封不动的发送回去。"""
    # 导入socket库
    #import socket
    from socket import *
    
    # 主机地址为0.0.0.0,表示绑定本机所有网络接口IP地址
    # 等待客户端来连接
    IP='127.0.0.1'
    #端口号
    PORT=6666
    #定义一次从socket缓冲区最多读入512个字节
    BUFLEN=512
    
    
    
    # 实例化一个socket对象
    # 调用socket中的socket()来创建一个listenSocket,主要用户进行监听
    # socket()有两个参数
    # 第一个参数AF_INET表示socket网络层使用IP协议
    # 第二参数SOCK_STREAM表示该socket传输层使用tcp协议
    listenSocket = socket(AF_INET,SOCK_STREAM)
    
    # bind()函数将我们创建的这个socket关联到我们主机的某一个网卡(网络接口,network interface)和端口上
    # socket绑定地址和端口
    listenSocket.bind((IP,PORT))
    
    # 使socket处于监听状态,等待客户端的连接请求
    # 参数5表示最多接受多少个等待连接的客户端
    listenSocket.listen(5)
    print(f'服务器端启动成功,在{PORT}端口等客户端的连接.....')
    
    dataSocket,addr=listenSocket.accept()
    print('接受一个客户端连接:',addr)
    
    while True:
        # 尝试读取对方发送的消息
        # BUFFEN指定从接受收缓存里最多读取多少字节
        recved = dataSocket.recv(BUFLEN)
    
        #如果返回空bytes,表示对方关闭了连接
        #退出循环,结束消息收发
        if not recved:
            break
    
        # 读取的字节数据是bytes 类型,需要解码为字符串
        info = recved.decode()
        print(f'收到对方信息: {info}')
    
        # 发送的数据类型必须为bytes,所以要编码
        dataSocket.send(f'服务端收到了信息 {info}'.encode())
    
        #服务端也调用close(),关闭socket
        #dataSocket.close()
        #listenSocket.close()
    
    
    
    • 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
  • 相关阅读:
    细胞实验中的对照给药
    LeetCode 每日一题——1710. 卡车上的最大单元数
    第4章 8088/8086指令系统
    悬崖边:企业如何应对网络安全漏洞趋势
    Milvus向量数据库:处理和分析大规模向量数据
    Spring Cloud Gateway系列【5】GatewayFilter网关过滤器详解
    【Gitee】生成与配置SSH公钥
    实现堆的各种基本运算的算法(数据结构)
    TypeScript断言
    积分商城该如何帮助商家盈利
  • 原文地址:https://blog.csdn.net/weixin_43134049/article/details/128185352