Python专家编程系列: 3. 善用装饰器使代码提高一个层次(Powerful Python Decorator)

2023-10-06 11:25:02 浏览数 (1)

0. 标题

Python专家编程系列: 3. 善用装饰器使代码提高一个层次(Powerful Python Decorator)

代码语言:txt复制
作者: quantgalaxy@outlook.com   
blog: https://blog.csdn.net/quant_galaxy  
欢迎交流

1. 常用的装饰器

1.1 cache

请注意,它仅适用于 Python >= 3.9。

这样就可以缓存以前的值并重复使用,而不是重新计算。

代码语言:python代码运行次数:0复制
from functools import cache

@cache
def factorial(n):
    return n * factorial(n-1) if n else 1

print(factorial(10))

Output:

代码语言:shell复制
3628800

这个装饰器,把factorial的每个步骤的值都记录下来了,在递归的过程中,就可以重复使用,并且在下次访问中也可以重复使用。

1.2 lru_cache

和cache类似,不过有一个maxsize参数,用来设置缓存的最大数量,避免内存膨胀。

当缓存已满并且需要存储新结果时,最近最少使用的结果将从缓存中逐出以为新结果腾出空间。这称为最近最少使用 (LRU) 策略。

默认情况下,maxsize 设置为 128。如果将其设置为 None,如我们的示例,LRU 功能将被禁用,并且缓存可以无限增长。

代码语言:python代码运行次数:0复制
from functools import lru_cache
import time


@lru_cache(maxsize=None)
def fibonacci(n):
    if n < 2:
        return n
    return fibonacci(n - 1)   fibonacci(n - 2)


start_time = time.perf_counter()
print(fibonacci(30))
end_time = time.perf_counter()
print(f"The execution time: {end_time - start_time:.8f} seconds")
# The execution time: 0.000023434 seconds

1.3 wraps

@wraps修饰器用于在修饰函数或方法后保留其元数据。

这对于调试、内省和文档目的非常有用。

比如下面这个,加入调试信息的使用:

代码语言:python代码运行次数:0复制
from functools import wraps

def debug(func):
    @wraps(func)
    def wrapper(*args, **kwargs):
        print(f"Calling {func.__name__} with args={args} kwargs={kwargs}")
        result = func(*args, **kwargs)
        print(f"{func.__name__} returned {result}")
        return result
    return wrapper


@debug
def my_function(x, y):
    return x   y

my_function(1, 2) # prints "Calling my_function with args=(1, 2) kwargs={}" and "my_function returned 3"

下面这个例子展示了使用这个装饰器后,原函数的内置变量都被保留了下来:

代码语言:python代码运行次数:0复制
from functools import wraps

def my_decorator(func):
    @wraps(func)
    def wrapper(*args, **kwargs):
        # do something before
        result = func(*args, **kwargs)
        # do something after
        return result
    return wrapper


@my_decorator
def my_function():
    """My function docstring"""
    pass


print(my_function.__name__) # my_function
print(my_function.__doc__) # My function docstring
代码语言:txt复制
作者: quantgalaxy@outlook.com   
blog: https://blog.csdn.net/quant_galaxy  
欢迎交流

1.4 timeit

@timeit装饰器用于测量函数的执行时间并将其打印到控制台。

这对于分析代码和查找性能瓶颈非常有用。

代码语言:python代码运行次数:0复制
import time
from functools import wraps

def timeit(func):
    @wraps(func)
    def wrapper(*args, **kwargs):
        start = time.perf_counter()
        result = func(*args, **kwargs)
        end = time.perf_counter()
        print(f'{func.__name__} took {end - start:.6f} seconds to complete')
        return result
    return wrapper


@timeit
def process_data():
    time.sleep(1)

process_data()
# process_data took 1.000012 seconds to complete

1.5 contextmanager

@contextmanager装饰器用于定义一个函数,该函数可用作带有 with 语句的上下文管理器。

这对于自动获取和释放资源(如文件句柄或锁)非常有用。

代码语言:python代码运行次数:0复制
from contextlib import contextmanager

@contextmanager
def my_context_manager():
    # acquire resource
    yield
    # release resource

with my_context_manager():
    # use resource
    pass

1.6 atexit.register

atexit 模块的 @register 装饰器可以让我们在 Python 解释器退出时执行一个函数。

这个装饰器对于执行最终任务非常有用,例如释放资源或只是打印调试信息:

代码语言:python代码运行次数:0复制
import atexit

@atexit.register
def goodbye():
    print("Bye bye!")

print("Hello Tim!")

Output:

代码语言:shell复制
Hello Tim!
Bye bye!

2. 作者信息

代码语言:txt复制
作者: quantgalaxy@outlook.com   
blog: https://blog.csdn.net/quant_galaxy  
欢迎交流

0 人点赞