理解Python的装饰器 decorator

既然是装饰器, 肯定就是跟设计模式中的装饰模式有关了

装饰器就是python 中对装饰模式的一种注解实现,令其可以对其他函数进行一些功能上的增强。
住这里的增强并不是真正的修改函数的业务逻辑, 只是前后增加一些步骤。
有点类似于java 的AOP(基于动态代理模式)

装饰模式的介绍:
https://blog.csdn.net/nvd11/article/details/41918155?ops_request_misc=%257B%2522request%255Fid%2522%253A%2522171493094016800184114116%2522%252C%2522scm%2522%253A%252220140713.130102334.pc%255Fblog.%2522%257D&request_id=171493094016800184114116&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2blogfirst_rank_ecpm_v1~rank_v31_ecpm-1-41918155-null-null.nonecase&utm_term=%E8%A3%85%E9%A5%B0&spm=1018.2226.3001.4450




编写1个函数, 可以build 1个100万个随即数字的数组

import random
from loguru import logger
import tracemalloc
import time

def sample1():
    # build a list of 1000000 numbers ramdomly
    list_num = [get_random_number(100) for _ in range(2000000)]
    logger.info("len of list_num is {}".format(len(list_num)))

def get_random_number(limit):
    return random.randint(0, limit)

if __name__ == "__main__":
    import src.configs.config
    sample1()

执行:

2024-05-06 01:29:13.314 | INFO     | __main__:sample1:7 - len of list_num is 10000000




需求, 修改这个函数, 在执行开始前输出执行开始时间, 执行后输出执行多长时间以及内存占用

那我们修改一下:

import random
from loguru import logger
import tracemalloc
import time

def sample2():
    tracemalloc.start()
    start_time = time.time()
    logger.info("Start time is {}".format(time.strftime("%Y-%m-%d %H:%M:%S", time.localtime(start_time))))


     # build a list of 1000000 numbers ramdomly
    list_num = [get_random_number(100) for _ in range(2000000)]
    logger.info("len of list_num is {}".format(len(list_num)))

    # print the memory usage
    current_mem, peak_mem = tracemalloc.get_traced_memory();
    logger.info("Current memory usage is {}MB; Peak was {}MB".format(current_mem / 10**6, peak_mem / 10**6))
    tracemalloc.stop

    # print the time usage
    end_time = time.time()
    logger.info("End time is {}".format(time.strftime("%Y-%m-%d %H:%M:%S", time.localtime(end_time))))
    logger.info("Time used is {} seconds".format(end_time - start_time))


def get_random_number(limit):
    return random.randint(0, limit)

if __name__ == "__main__":
    import src.configs.config
    sample2()

输出:

import random
from loguru import logger
import tracemalloc
import time

def sample1():
    # build a list of 1000000 numbers ramdomly
    list_num = [get_random_number(100) for _ in range(2000000)]
    logger.info("len of list_num is {}".format(len(list_num)))


def get_random_number(limit):
    return random.randint(0, limit)

if __name__ == "__main__":
    import src.configs.config
    sample1()

咋一看不怎么复杂, 无非在函数内的核心代码前后添加一些 time 和 mem usage 的信息收集代码
但是假如你的line manager 要求你把某个项目的多个函数都加上这个功能。 那你怎么办, 一个一个函数去修改吗?

这时就可以利用装饰器去实现了




python 可以另1个函数 作为另1个函数的一个参数

跟javascript 一样, 而不需要像java 那样利用接口实现
举个例子:

def sample_callback():
    rs = cal_numbers(1, 2 ,3, call_back=avg_call_back)
    logger.info("avg is {}".format(rs))
    rs = cal_numbers(1, 2 ,3, 4, call_back=max_call_back)
    logger.info("max num is {}".format(rs))
    rs = cal_numbers(1, 2, 3, 4, 5 , call_back=lambda *args: sum(args) / len(args))
    logger.info("avg is {}".format(rs))

def cal_numbers(*args, call_back = None):
    if call_back:
        return call_back(*args)
    else:
        return sum(args)
    pass

def avg_call_back(*args):
    return sum(args) / len(args)
    
def max_call_back(*args):
    return max(args)

if __name__ == "__main__":
    import src.configs.config
    sample_callback()

上面例子中, avg_call_back 函数 he max_call_back 就是作为1个参数传入 cal_numbers
甚至可以用lambda 构建1个 匿名函数作为参数

这个特性是构造python 装饰器的一个前提




构建1个简单的装饰器

from loguru import logger

def print_info(func):
    logger.info("print_info is used for function {}".format(func.__name__))
    def wrapper(*args, **kwargs):

        logger.info.infont("Function {} is going to be called".format(func.__name__))
        rs = func(*args, **kwargs)
        logger.info("Function {} is called".format(func.__name__))
        return rs
    logger.info("print_info is defined done for function {}".format(func.__name__))
    return wrapper

如何使用这个装饰器呢
第一个方法是 用装饰器去包装原方法, 因为print_info 的参数是1个函数嘛
例子:

import random
from loguru import logger
import tracemalloc
import time

from src.decorator.print_info import print_info

def sample3_1():
     # build a list of 2000000 numbers ramdomly
    list_num = [get_random_number(100) for _ in range(2000000)]
    logger.info("len of list_num is {}".format(len(list_num)))

def sample3_2():
     # build a list of 3000000 numbers ramdomly
    list_num = [get_random_number(100) for _ in range(3000000)]
    logger.info("len of list_num is {}".format(len(list_num)))

def run_sample3():
    sample = print_info(sample3_1)
    sample()
    sample = print_info(sample3_2)
    sample()


def get_random_number(limit):
    return random.randint(0, limit)

if __name__ == "__main__":
    import src.configs.config
    run_sample3()

输出:

2024-05-06 03:50:29.334 | INFO     | src.configs.config:<module>:19 - basic setup done
2024-05-06 03:50:29.334 | INFO     | src.configs.config:<module>:27 - all configs loaded
2024-05-06 03:50:29.334 | INFO     | src.decorator.print_info:print_info:4 - print_info is used for function sample3_1
2024-05-06 03:50:29.334 | INFO     | src.decorator.print_info:print_info:11 - print_info is defined done for function sample3_1
2024-05-06 03:50:29.335 | INFO     | src.decorator.print_info:wrapper:7 - Function sample3_1 is going to be called
2024-05-06 03:50:29.974 | INFO     | __main__:sample3_1:11 - len of list_num is 2000000
2024-05-06 03:50:29.978 | INFO     | src.decorator.print_info:wrapper:9 - Function sample3_1 is called
2024-05-06 03:50:29.979 | INFO     | src.decorator.print_info:print_info:4 - print_info is used for function sample3_2
2024-05-06 03:50:29.979 | INFO     | src.decorator.print_info:print_info:11 - print_info is defined done for function sample3_2
2024-05-06 03:50:29.979 | INFO     | src.decorator.print_info:wrapper:7 - Function sample3_2 is going to be called
2024-05-06 03:50:30.932 | INFO     | __main__:sample3_2:16 - len of list_num is 3000000
2024-05-06 03:50:30.935 | INFO     | src.decorator.print_info:wrapper:9 - Function sample3_2 is called

注意日志的打印顺序
先是print_info defined
然后才是 going to be called
然后函数执行
最后是function is called




使用注解来调用装饰器

其实上面的写法
sample = print_info(sample3_1)
sample()
sample = print_info(sample3_2)
sample()

调用装饰器的方法并不常用, 在项目中也不是很方便, 还是要查找被修改的函数的所有引用, 可能还是会改动 很多个地方。
实际上, 装饰器是用在注解上的

修改后代码如下:

import random
from loguru import logger
import tracemalloc
import time

from src.decorator.print_info import print_info

@print_info
def sample4_1():
     # build a list of 1000000 numbers ramdomly
    list_num = [get_random_number(100) for _ in range(2000000)]
    logger.info("len of list_num is {}".format(len(list_num)))

@print_info
def sample4_2():
     # build a list of 1000000 numbers ramdomly
    list_num = [get_random_number(100) for _ in range(3000000)]
    logger.info("len of list_num is {}".format(len(list_num)))


def get_random_number(limit):
    return random.randint(0, limit)

if __name__ == "__main__":
    import src.configs.config
    sample4_1()
    sample4_2()

这次直接把print_info 作为 注解添加到要使用的函数上
实际上python的@注解都是装饰器

输出:

2024-05-06 03:51:03.226 | INFO     | src.decorator.print_info:print_info:4 - print_info is used for function sample4_1
2024-05-06 03:51:03.227 | INFO     | src.decorator.print_info:print_info:11 - print_info is defined done for function sample4_1
2024-05-06 03:51:03.227 | INFO     | src.decorator.print_info:print_info:4 - print_info is used for function sample4_2
2024-05-06 03:51:03.227 | INFO     | src.decorator.print_info:print_info:11 - print_info is defined done for function sample4_2
project_path is /home/gateman/Projects/python/python_common_import
2024-05-06 03:51:03.242 | INFO     | src.configs.config:<module>:19 - basic setup done
2024-05-06 03:51:03.243 | INFO     | src.configs.config:<module>:27 - all configs loaded
2024-05-06 03:51:03.243 | INFO     | src.decorator.print_info:wrapper:7 - Function sample4_1 is going to be called
2024-05-06 03:51:03.886 | INFO     | __main__:sample4_1:12 - len of list_num is 2000000
2024-05-06 03:51:03.890 | INFO     | src.decorator.print_info:wrapper:9 - Function sample4_1 is called
2024-05-06 03:51:03.890 | INFO     | src.decorator.print_info:wrapper:7 - Function sample4_2 is going to be called
2024-05-06 03:51:04.856 | INFO     | __main__:sample4_2:18 - len of list_num is 3000000
2024-05-06 03:51:04.858 | INFO     | src.decorator.print_info:wrapper:9 - Function sample4_2 is called

注意这次print 的顺序
因为是注解方式, 所以python在执行代码之前首先执行了 print_info的定义部分, 而这时被包装的函数还没有调用, 容器理解




为装饰器添加1层装饰器

上面的装饰器 这是在函数执行前后print 一下信息

如果再增加1个功能, 我们其实可以通过增加装饰器来实现的
我们增加1个print_time的装饰器
print_time.py

from loguru import logger
import time
def print_time(func):
    def wrapper(*args, **kwargs):
        start_time = time.time()
        logger.info("Start time of {} is {}".format(func.__name__, time.strftime("%Y-%m-%d %H:%M:%S", time.localtime(start_time))))
        rs = func(*args, **kwargs)
        end_time = time.time()
        logger.info("End time of {} is {}".format(func.__name__, time.strftime("%Y-%m-%d %H:%M:%S", time.localtime(end_time))))
        logger.info("Time used of {} is {} seconds".format(func.__name__, end_time - start_time))
        return rs
    return wrapper
   

如何调用呢
第一种调用方法:

import random
from loguru import logger
import tracemalloc
import time

from src.decorator.print_info import print_info
from src.decorator.print_time import print_time

def sample5():
     # build a list of 2000000 numbers ramdomly
    list_num = [get_random_number(100) for _ in range(2000000)]
    logger.info("len of list_num is {}".format(len(list_num)))


def run_sample5():
    sample = print_time(print_info(sample5))
    sample()
    logger.info("====================================")
    sample = print_info(print_time(sample5))
    sample()

def get_random_number(limit):
    return random.randint(0, limit)

if __name__ == "__main__":
    import src.configs.config
    run_sample5()

注意 这里调用了两次sample5, 分别用print_time, print_info一起包装,但是包装的顺序不同的

输出:

2024-05-06 03:57:50.293 | INFO     | src.decorator.print_info:print_info:4 - print_info is used for function sample5
2024-05-06 03:57:50.294 | INFO     | src.decorator.print_info:print_info:11 - print_info is defined done for function sample5
2024-05-06 03:57:50.294 | INFO     | src.decorator.print_time:wrapper:6 - Start time of wrapper is 2024-05-06 03:57:50
2024-05-06 03:57:50.294 | INFO     | src.decorator.print_info:wrapper:7 - Function sample5 is going to be called
2024-05-06 03:57:50.934 | INFO     | __main__:sample5:12 - len of list_num is 2000000
2024-05-06 03:57:50.939 | INFO     | src.decorator.print_info:wrapper:9 - Function sample5 is called
2024-05-06 03:57:50.939 | INFO     | src.decorator.print_time:wrapper:9 - End time of wrapper is 2024-05-06 03:57:50
2024-05-06 03:57:50.939 | INFO     | src.decorator.print_time:wrapper:10 - Time used of wrapper is 0.6454496383666992 seconds
2024-05-06 03:57:50.939 | INFO     | __main__:run_sample5:18 - ====================================
2024-05-06 03:57:50.939 | INFO     | src.decorator.print_info:print_info:4 - print_info is used for function wrapper
2024-05-06 03:57:50.939 | INFO     | src.decorator.print_info:print_info:11 - print_info is defined done for function wrapper
2024-05-06 03:57:50.940 | INFO     | src.decorator.print_info:wrapper:7 - Function wrapper is going to be called
2024-05-06 03:57:50.940 | INFO     | src.decorator.print_time:wrapper:6 - Start time of sample5 is 2024-05-06 03:57:50
2024-05-06 03:57:51.620 | INFO     | __main__:sample5:12 - len of list_num is 2000000
2024-05-06 03:57:51.622 | INFO     | src.decorator.print_time:wrapper:9 - End time of sample5 is 2024-05-06 03:57:51
2024-05-06 03:57:51.622 | INFO     | src.decorator.print_time:wrapper:10 - Time used of sample5 is 0.682539701461792 seconds
2024-05-06 03:57:51.623 | INFO     | src.decorator.print_info:wrapper:9 - Function wrapper is called

很明显, 第一次是 print time 外层, print_info 内层
第二次相反,
对应的调用次序也是不同的
但是有个问题, 外层的装饰器并不能正确获取func._name_ , 其实也不难理解, 因为对于外层里装饰器,参数就是内层的装饰器了, 名字就是wrapper…




解决外层装饰器不能获取源函数名字的问题 - @functools.wraps(func)

很简单, 在内层的装饰器上添加1个 官方装饰器
@functools.wraps(func)

修改后的print_info, 和 print_time

import functools
from loguru import logger

def print_info(func):
    logger.info("print_info is used for function {}".format(func.__name__))
    
    @functools.wraps(func)
    def wrapper(*args, **kwargs):

        logger.info("Function {} is going to be called".format(func.__name__))
        rs = func(*args, **kwargs)
        logger.info("Function {} is called".format(func.__name__))
        return rs
    logger.info("print_info is defined done for function {}".format(func.__name__))
    return wrapper
from loguru import logger
import time
import functools

def print_time(func):
    @functools.wraps(func)
    def wrapper(*args, **kwargs):
        start_time = time.time()
        logger.info("Start time of {} is {}".format(func.__name__, time.strftime("%Y-%m-%d %H:%M:%S", time.localtime(start_time))))
        rs = func(*args, **kwargs)
        end_time = time.time()
        logger.info("End time of {} is {}".format(func.__name__, time.strftime("%Y-%m-%d %H:%M:%S", time.localtime(end_time))))
        logger.info("Time used of {} is {} seconds".format(func.__name__, end_time - start_time))
        return rs
    return wrapper
  

再次执行测试代码:

2024-05-06 04:04:27.783 | INFO     | src.decorator.print_info:print_info:5 - print_info is used for function sample5
2024-05-06 04:04:27.783 | INFO     | src.decorator.print_info:print_info:14 - print_info is defined done for function sample5
2024-05-06 04:04:27.783 | INFO     | src.decorator.print_time:wrapper:9 - Start time of sample5 is 2024-05-06 04:04:27
2024-05-06 04:04:27.783 | INFO     | src.decorator.print_info:wrapper:10 - Function sample5 is going to be called
2024-05-06 04:04:28.422 | INFO     | __main__:sample5:12 - len of list_num is 2000000
2024-05-06 04:04:28.427 | INFO     | src.decorator.print_info:wrapper:12 - Function sample5 is called
2024-05-06 04:04:28.427 | INFO     | src.decorator.print_time:wrapper:12 - End time of sample5 is 2024-05-06 04:04:28
2024-05-06 04:04:28.427 | INFO     | src.decorator.print_time:wrapper:13 - Time used of sample5 is 0.6437935829162598 seconds
2024-05-06 04:04:28.427 | INFO     | __main__:run_sample5:18 - ====================================
2024-05-06 04:04:28.427 | INFO     | src.decorator.print_info:print_info:5 - print_info is used for function sample5
2024-05-06 04:04:28.427 | INFO     | src.decorator.print_info:print_info:14 - print_info is defined done for function sample5
2024-05-06 04:04:28.427 | INFO     | src.decorator.print_info:wrapper:10 - Function sample5 is going to be called
2024-05-06 04:04:28.427 | INFO     | src.decorator.print_time:wrapper:9 - Start time of sample5 is 2024-05-06 04:04:28
2024-05-06 04:04:29.096 | INFO     | __main__:sample5:12 - len of list_num is 2000000
2024-05-06 04:04:29.099 | INFO     | src.decorator.print_time:wrapper:12 - End time of sample5 is 2024-05-06 04:04:29
2024-05-06 04:04:29.099 | INFO     | src.decorator.print_time:wrapper:13 - Time used of sample5 is 0.6715624332427979 seconds
2024-05-06 04:04:29.099 | INFO     | src.decorator.print_info:wrapper:12 - Function sample5 is calle

问题解决




用注解方式实现多层装饰器

也很简单, 一齐把注解写上去就行

import random
from loguru import logger
import tracemalloc
import time

from src.decorator.print_info import print_info
from src.decorator.print_time import print_time

@print_info
@print_time
def sample6_1():
     # build a list of 1000000 numbers ramdomly
    list_num = [get_random_number(100) for _ in range(2000000)]
    logger.info("len of list_num is {}".format(len(list_num)))

@print_time
@print_info
def sample6_2():
     # build a list of 1000000 numbers ramdomly
    list_num = [get_random_number(100) for _ in range(3000000)]
    logger.info("len of list_num is {}".format(len(list_num)))


def get_random_number(limit):
    return random.randint(0, limit)

if __name__ == "__main__":
    import src.configs.config
    sample6_1()
    logger.info("====================================")
    sample6_2()

同样, 添加注解的顺序也是有影响的
输出:

2024-05-06 04:08:17.287 | INFO     | src.configs.config:<module>:19 - basic setup done
2024-05-06 04:08:17.287 | INFO     | src.configs.config:<module>:27 - all configs loaded
2024-05-06 04:08:17.287 | INFO     | src.decorator.print_info:wrapper:10 - Function sample6_1 is going to be called
2024-05-06 04:08:17.287 | INFO     | src.decorator.print_time:wrapper:9 - Start time of sample6_1 is 2024-05-06 04:08:17
2024-05-06 04:08:17.916 | INFO     | __main__:sample6_1:14 - len of list_num is 2000000
2024-05-06 04:08:17.920 | INFO     | src.decorator.print_time:wrapper:12 - End time of sample6_1 is 2024-05-06 04:08:17
2024-05-06 04:08:17.920 | INFO     | src.decorator.print_time:wrapper:13 - Time used of sample6_1 is 0.6329360008239746 seconds
2024-05-06 04:08:17.920 | INFO     | src.decorator.print_info:wrapper:12 - Function sample6_1 is called
2024-05-06 04:08:17.921 | INFO     | __main__:<module>:30 - ====================================
2024-05-06 04:08:17.921 | INFO     | src.decorator.print_time:wrapper:9 - Start time of sample6_2 is 2024-05-06 04:08:17
2024-05-06 04:08:17.921 | INFO     | src.decorator.print_info:wrapper:10 - Function sample6_2 is going to be called
2024-05-06 04:08:18.907 | INFO     | __main__:sample6_2:21 - len of list_num is 3000000
2024-05-06 04:08:18.911 | INFO     | src.decorator.print_info:wrapper:12 - Function sample6_2 is called
2024-05-06 04:08:18.911 | INFO     | src.decorator.print_time:wrapper:12 - End time of sample6_2 is 2024-05-06 04:08:18
2024-05-06 04:08:18.911 | INFO     | src.decorator.print_time:wrapper:13 - Time used of sample6_2 is 0.9907040596008301 seconds




合并多个装饰器成1个装饰器

如果嫌每个方法写多个@注解麻烦
我们可以再添加1个装饰器, 包含print_info 和 print_time
print_info_time.py

import functools
from loguru import logger

from src.decorator.print_info import print_info
from src.decorator.print_time import print_time


def print_info_time(func):

    @print_info
    @print_time
    @functools.wraps(func)
    def wrapper(*args, **kwargs):
        rs = func(*args, **kwargs)
        return rs

    return wrapper

在这里我们不再需要重写 业务逻辑, 直接引用 @print_info 和 @print_time就好
当然不想用注解, return wrapper 改成 return print_info(print_time(wrapper)) 也可以

测试:

import random
from loguru import logger

from src.decorator.print_info_time import print_info_time

@print_info_time
def sample7():
     # build a list of 1000000 numbers ramdomly
    list_num = [get_random_number(100) for _ in range(2000000)]
    logger.info("len of list_num is {}".format(len(list_num)))


def get_random_number(limit):
    return random.randint(0, limit)

if __name__ == "__main__":
    import src.configs.config
    sample7()

输出:

2024-05-06 04:24:58.113 | INFO     | src.configs.config:<module>:19 - basic setup done
2024-05-06 04:24:58.113 | INFO     | src.configs.config:<module>:27 - all configs loaded
2024-05-06 04:24:58.113 | INFO     | src.decorator.print_info:wrapper:10 - Function sample7 is going to be called
2024-05-06 04:24:58.113 | INFO     | src.decorator.print_time:wrapper:9 - Start time of sample7 is 2024-05-06 04:24:58
2024-05-06 04:24:58.744 | INFO     | __main__:sample7:10 - len of list_num is 2000000
2024-05-06 04:24:58.749 | INFO     | src.decorator.print_time:wrapper:12 - End time of sample7 is 2024-05-06 04:24:58
2024-05-06 04:24:58.749 | INFO     | src.decorator.print_time:wrapper:13 - Time used of sample7 is 0.6352584362030029 seconds
2024-05-06 04:24:58.749 | INFO     | src.decorator.print_info:wrapper:12 - Function sample7 is called




为装饰器添加参数

需求很简单, 在print_time 的装饰器上, 让其接受1个参数 second_limit , 如果 执行时间大于这个指, 则给个warning

这时就要在print_time 装饰器的代码level增加1层了…
修改后

from loguru import logger
import time
import functools

def print_time(second_limit=500):
    def print_time_inner(func):
        @functools.wraps(func)
        def wrapper(*args, **kwargs):
            start_time = time.time()
            logger.info("Start time of {} is {}".format(func.__name__, time.strftime("%Y-%m-%d %H:%M:%S", time.localtime(start_time))))
            rs = func(*args, **kwargs)
            end_time = time.time()
            logger.info("End time of {} is {}".format(func.__name__, time.strftime("%Y-%m-%d %H:%M:%S", time.localtime(end_time))))
            logger.info("Time used of {} is {} seconds".format(func.__name__, end_time - start_time))
            if end_time - start_time > second_limit:
                logger.warning("Time used of {} is more than {} seconds".format(func.__name__, second_limit))
            return rs
        return wrapper
    return print_time_inner

测试:

import random
from loguru import logger

from src.decorator.print_time import print_time

@print_time(second_limit=1)
def sample8():
     # build a list of 3000000 numbers ramdomly
    list_num = [get_random_number(100) for _ in range(4000000)]
    logger.info("len of list_num is {}".format(len(list_num)))


def get_random_number(limit):
    return random.randint(0, limit)

if __name__ == "__main__":
    import src.configs.config
    sample8()

输出:

2024-05-06 05:14:58.547 | INFO     | src.configs.config:<module>:19 - basic setup done
2024-05-06 05:14:58.547 | INFO     | src.configs.config:<module>:27 - all configs loaded
2024-05-06 05:14:58.547 | INFO     | src.decorator.print_time:wrapper:10 - Start time of sample8 is 2024-05-06 05:14:58
2024-05-06 05:14:59.917 | INFO     | __main__:sample8:10 - len of list_num is 4000000
2024-05-06 05:14:59.925 | INFO     | src.decorator.print_time:wrapper:13 - End time of sample8 is 2024-05-06 05:14:59
2024-05-06 05:14:59.925 | INFO     | src.decorator.print_time:wrapper:14 - Time used of sample8 is 1.3772785663604736 seconds
2024-05-06 05:14:59.925 | WARNING  | src.decorator.print_time:wrapper:16 - Time used of sample8 is more than 1 seconds

Note:
如果 1个装饰器带参数, 则与其他装饰器混用时, 最好带参数, 如果想用默认参数就带()
例如:

def run_sample5():
    sample = print_time()(print_info(sample5))
    sample()
    logger.info("====================================")
    sample = print_info(print_time()(sample5))
    sample()

注意上面代码print_time() return1个带默认参数的 print_time装饰器 , 然后才接收后面的函数参数

同样的注解也是:

@print_info
@print_time() # 不带括号会出错
def sample6_1():
     # build a list of 1000000 numbers ramdomly
    list_num = [get_random_number(100) for _ in range(2000000)]
    logger.info("len of list_num is {}".format(len(list_num)))




用装饰器来实现 统计时间和内存的需求

好了, 终于要实现场景了

其实我们已经写好print_time
我们只需要写多1个print_mem
和1个包住上面两个装饰器的总装饰器 sum_info 就好
print_mem.py

from loguru import logger
import functools
import tracemalloc

def print_mem(func):

    @functools.wraps(func)
    def wrapper(*args, **kwargs):
        tracemalloc.start()

        rs = func(*args, **kwargs)
         # print the memory usage
        current_mem, peak_mem = tracemalloc.get_traced_memory();
        logger.info("Current memory usage is {}MB; Peak was {}MB".format(current_mem / 10**6, peak_mem / 10**6))
        tracemalloc.stop
        return rs
    return wrapper

sum_info.py

import functools
from loguru import logger

from src.decorator.print_mem import print_mem
from src.decorator.print_time import print_time


def sum_info(func):

    @print_time() # here must provide the () otherwise TypeError: print_time.<locals>.decorator() missing 1 required positional argument: 'func'
    @print_mem
    @functools.wraps(func)
    def wrapper(*args, **kwargs):
        rs = func(*args, **kwargs)
        return rs

    return wrapper

测试:

import random
from loguru import logger

from src.decorator.sum_info import sum_info

@sum_info
def sample9():
     # build a list of 3000000 numbers ramdomly
    list_num = [get_random_number(100) for _ in range(4000000)]
    logger.info("len of list_num is {}".format(len(list_num)))


def get_random_number(limit):
    return random.randint(0, limit)

if __name__ == "__main__":
    import src.configs.config
    sample9()

输出:

2024-05-06 05:25:04.225 | INFO     | src.configs.config:<module>:19 - basic setup done
2024-05-06 05:25:04.226 | INFO     | src.configs.config:<module>:27 - all configs loaded
2024-05-06 05:25:04.226 | INFO     | src.decorator.print_time:wrapper:10 - Start time of sample9 is 2024-05-06 05:25:04
2024-05-06 05:25:07.177 | INFO     | __main__:sample9:10 - len of list_num is 4000000
2024-05-06 05:25:07.183 | INFO     | src.decorator.print_mem:wrapper:14 - Current memory usage is 0.000394MB; Peak was 34.732609MB
2024-05-06 05:25:07.184 | INFO     | src.decorator.print_time:wrapper:13 - End time of sample9 is 2024-05-06 05:25:07
2024-05-06 05:25:07.185 | INFO     | src.decorator.print_time:wrapper:14 - Time used of sample9 is 2.9584157466888428 seconds

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mfbz.cn/a/594234.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Python 全栈系列241 GFGo Lite迭代

说明 随着整个算网开发逐渐深入&#xff0c;各个组件、微服务的数量、深度在不断增加。由于算网是个人项目&#xff0c;我一直按照MVP(Minimum Viable Product )的原则在推进。由于最初的时候对架构、算法和业务的理解并没有那么深刻&#xff0c;所以MVP的内容还是在不断变化&…

选择深度学习框架:TensorFlow 2 vs PyTorch

TensorFlow 2 vs PyTorch 选择深度学习框架&#xff1a;TensorFlow 2 vs PyTorchTensorFlow 2概述TensorFlow 2的优点TensorFlow 2的缺点 PyTorch概述PyTorch的优点PyTorch的缺点 选择建议对于选择困难症的人&#xff0c;我给你们的答案——PyTorch选择理由&#xff1a;结论&am…

数据结构(C):玩转链表

&#x1f37a;0.前言 言C之言&#xff0c;聊C之识&#xff0c;以C会友&#xff0c;共向远方。各位博友的各位你们好啊&#xff0c;这里是持续分享数据结构知识的小赵同学&#xff0c;今天要分享的数据结构知识是链表&#xff0c;在这一章&#xff0c;小赵将会向大家展开聊聊链表…

常用语音识别开源四大工具:Kaldi,PaddleSpeech,WeNet,EspNet

无论是基于成本效益还是社区支持&#xff0c;我都坚决认为开源才是推动一切应用的动力源泉。下面推荐语音识别开源工具&#xff1a;Kaldi&#xff0c;Paddle&#xff0c;WeNet&#xff0c;EspNet。 1、最成熟的Kaldi 一个广受欢迎的开源语音识别工具&#xff0c;由Daniel Pove…

Servlet框架

简介 Servlet是运行在web服务器或应用服务器上的程序&#xff0c;他是作为来自web浏览器或其他http客户端的请求和HTTP服务器上的数据库或应用程序之间的中间层。 使用Servlet可以手机来自网页表单的用户输入&#xff0c;呈现来自数据库或者其他源记录&#xff0c;还可以动态创…

IDEA访问不到静态资源

背景 我在resources下创建static文件夹&#xff0c;再创建front文件夹放前端资源&#xff0c;里面有index.html&#xff0c;游览器输入localhost:8011/front没反应。&#xff08;resources/static/front/index.html&#xff09; 解决办法 重启idea&#xff0c;清楚idea缓存&am…

设计模式之服务定位器模式

想象一下&#xff0c;你的Java应用是一座庞大的迷宫&#xff0c;里面藏着无数宝贵的服务宝藏&#xff0c;而你正需要一张精确的藏宝图来指引方向&#xff0c;迅速找到并利用这些宝藏。服务定位器模式&#xff0c;正是这样一张神奇的地图&#xff0c;它帮你动态定位并获取应用中…

stl容器 string类的基本操作

目录 一.string类的构造 二.string类的输出 1.传统字符串输出 2.通过迭代器进行输出 ​编辑 3.C11标准的范围for输出加auto推导类型 三.string类的各种迭代器 begin(&#xff09;和end() 利用迭代器遍历输出 利用迭代器修改字符串的字符 rbgin()和rend() 利用迭代器遍…

[论文阅读]Adversarial Autoencoders(aae)和代码

In this paper, we propose the “adversarial autoencoder” (AAE), which is a probabilistic autoencoder that uses the recently proposed generative adversarial networks (GAN) to perform variational inference by matching the aggregated posterior of the hidden …

【人工智能基础】RNN实验

一、RNN特性 权重共享 wordi weight bais 持久记忆单元 wordi weightword baisword hi weighth baish 二、公式化表达 ht</sub f(ht - 1, xt) ht tanh(Whhht - 1 Wxhxt) yt Whyht 三、RNN网络正弦波波形预测 环境准备 import numpy as np import torch …

服务器端优化-Redis内存划分和内存配置

6、服务器端优化-Redis内存划分和内存配置 当Redis内存不足时&#xff0c;可能导致Key频繁被删除、响应时间变长、QPS不稳定等问题。当内存使用率达到90%以上时就需要我们警惕&#xff0c;并快速定位到内存占用的原因。 有关碎片问题分析 Redis底层分配并不是这个key有多大&…

PG 全页写

1.什么是全页写 修改一个块的时候&#xff0c;把块读到内存中&#xff0c;commit后,WAL写进程会触发写&#xff0c;把修改的块写到WAL日志文件&#xff0c;如果再往这个块中插入一条数据&#xff0c;数据缓冲区里面的块有两条数据了&#xff0c;再次commit后&#xff0c;PG会把…

图像处理--空域滤波增强(原理)

一、均值滤波 线性滤波算法&#xff0c;采用的主要是邻域平均法。基本思想是使用几个像素灰度的某种平均值来代替一个原来像素的灰度值。可以新建一个MN的窗口以为中心&#xff0c;这个窗口S就是的邻域。假设新的新的像素灰度值为&#xff0c;则计算公式为 1.1 简单平均法 就是…

在excel中,alt+13和alt+10都是什么字符?

1.回车符与换行符 Alt13是回车符&#xff0c;Alt10是换行符。 2.用在microsoft word中 在microsoft office中&#xff0c;回车符 和 换行符 对文本来讲都有换行的作用&#xff0c;但它们并不是同一种符号。下图是在word中两种字符的显示&#xff0c; 当使用 回车符 进行文本…

Ubuntu MATE系统下WPS显示错位

系统&#xff1a;Ubuntu MATE 22.04和24.04&#xff0c;在显示器设置200%放大的情况下&#xff0c;显示错位。 显示器配置&#xff1a; WPS显示错位&#xff1a; 这个问题当前没有找到好的解决方式。 因为4K显示屏设置4K分辨率&#xff0c;图标&#xff0c;字体太小&#xff…

TCP(TCP客户端、服务器如何通信)

一、TCP介绍 TCP的特点&#xff1a; 面向连接的协议&#xff1a;TCP是一种可靠的、面向连接的协议&#xff0c;在通信之前需要建立连接&#xff0c;以确保数据的可靠传输。这意味着在传输数据之前&#xff0c;发送方和接收方之间需要建立一条可靠的连接通道。流式协议&#x…

Spring Cloud架构进化实操:Eureka、Apollo、OpenFeign、Ribbon、Zuul组件

文章目录 前言一、引出二、服务注册与发现2.1 创建Eureka注册中心2.1.1 引入pom依赖2.1.2 配置yaml2.1.3 启动服务21.4 测试访问 2.2 创建服务提供者2.2.1 配置yaml2.2.2 启动服务2.2.3 测试访问 2.3 创建服务消费者2.3.1 服务提供者接口2.3.2 服务消费者调用接口 三、负载均衡…

Docker的私有仓库部署-Harbor

目录 一. Docker原生私有仓库 Registry 1. Registry 的介绍 2. Registry 的部署过程 二. Registry 的升级——Habor 1. Harbor 简介 2. Harbor 特性 3. Harbor 的构成 4. Harbor 部署 4.1 部署 Docker-Compose 服务 4.2 部署 Harbor 服务 4.2.1 下载或上传 Harbor…

18_Scala面向对象编程trait

文章目录 trait1.定义trait2.向类中混入特质2.1没有父类2.2有父类 3.动态混入3.1动态混入查询功能到公司业务中 4.父类&#xff0c;子类&#xff0c;特质初始化优先级5.Scala功能执行顺序6.常用API trait –特质的学习需要类比Java中的接口&#xff0c;源码编译之后就是interf…

三种方法解决:检测到在集成的托管管道模式下不适用的 ASP.NET 设置

几天前配置一个IIS环境的网站时,出现500错误。根据错误提示,很快把问题解决了,现记录一下,希望能帮到遇到同样问题的网友。 问题描述 (点击图片放大) 应用程序“DEFAULT WEB SITE”中的服务器错误Internet Information Services 7.5错误摘要 HTTP 错误 500.24 - Interna…
最新文章