理解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
  • 34
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

nvd11

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值