欢迎访问宙启技术站
智能推送

Python的装饰器:如何将函数转换为具有附加功能的函数?

发布时间:2023-05-21 19:58:20

Python装饰器是Python语言中函数的一个重要概念,它允许在不改变函数本身的情况下,给函数增加新的功能,提高代码的重用性、灵活性和可维护性。装饰器通常以“@”符号来标记在函数定义的上方,可以对函数进行拦截、修改或者增强。在本篇文章中,我们将详细介绍Python装饰器以及如何将函数转换为具有附加功能的函数。

1. 什么是Python装饰器

在Python中,函数是一等公民(first-class citizen),意味着函数可以作为参数传递给其他函数、可以作为返回值返回给其他函数、可以赋值给变量、可以被嵌套定义在其他函数内等等。而装饰器本质上就是一个函数,它可以接收一个函数作为参数,并返回一个新的函数,同时在新函数中添加一些额外的代码,以实现更多的功能。

比如一个简单的装饰器,可以在函数调用前输出一行日志信息,如下所示:

def log_decorator(func):
    def wrapper(*args, **kwargs):
        print("This function is called with arguments", args, kwargs)
        return func(*args, **kwargs)
    return wrapper

@log_decorator
def add(x, y):
    return x + y

add(2, 3)    # 输出:This function is called with arguments (2, 3) {}
             #      返回:5

在上面的代码中,我们首先定义了一个log_decorator装饰器,它可以接收一个函数作为参数,并返回一个wrapper函数。wrapper函数是一个闭包,它包裹着原函数,并在执行原函数前后添加了一些额外的代码。在这个例子中,wrapper函数打印了函数调用时的参数,并返回原函数的执行结果。而在add函数上方,我们使用了“@”符号将log_decorator装饰器应用到了add函数上,相当于执行了add = log_decorator(add)的过程。

2. 如何实现一个Python装饰器

了解了Python装饰器的基本概念之后,我们可以来看看如何实现一个Python装饰器。一个Python装饰器至少要包含一个函数,具体的实现方式如下所示:

def my_decorator(func):
    def wrapper(*args, **kwargs):
        # 在调用 func 函数之前执行的代码
        result = func(*args, **kwargs)
        # 在调用 func 函数之后执行的代码
        return result
    return wrapper

在上面的代码中,我们首先定义了一个名为my_decorator的函数,它接收一个函数作为参数,并返回一个新的函数wrapper。wrapper函数也是一个闭包,它包裹着原始的函数,并在函数调用前后执行一些额外的代码。

在wrapper函数中,我们使用了*args和**kwargs来接收任意数量的位置参数和关键字参数,以保证装饰器可以应用于不同签名的函数。然后我们在执行原始函数前后添加了一些日志或者其他附加功能,最后返回函数的执行结果。

通常情况下,我们使用“@”符号来进行装饰器的语法糖,如下所示:

@my_decorator
def my_function():
    pass

这样可以省去了显式调用装饰器的步骤,使代码更加简洁、易读。

3. 如何使用Python装饰器实现附加功能

通过Python装饰器,我们可以非常方便地实现一些常见的附加功能,如下所示:

3.1 记录日志

在函数执行前后记录一些日志信息,以便调试和追踪程序的执行过程。比如我们可以定义一个log装饰器,如下所示:

def log(func):
    def wrapper(*args, **kwargs):
        print(f"[INFO] {func.__name__}() is called with ({args}, {kwargs})")
        result = func(*args, **kwargs)
        print(f"[INFO] {func.__name__}() return {result}")
        return result
    return wrapper

@log
def add(x, y):
    return x + y

add(2, 3)
# [INFO] add() is called with ((2, 3), {})
# [INFO] add() return 5

3.2 缓存结果

在函数结果不变的情况下,缓存结果可以提高函数执行速度,避免重复计算。比如我们可以定义一个cache装饰器,如下所示:

def cache(func):
    cache_dict = {}
    def wrapper(*args, **kwargs):
        key = str(args) + str(kwargs)
        if key in cache_dict:
            print(f"[CACHE] get result from cache")
            return cache_dict[key]
        result = func(*args, **kwargs)
        cache_dict[key] = result
        return result
    return wrapper

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

factorial(5)
factorial(5)    # [CACHE] get result from cache
factorial(6)
factorial(6)    # [CACHE] get result from cache

在上面的代码中,我们定义了一个cache装饰器,它实际上是一个闭包,其中定义了一个cache_dict字典用于缓存函数结果。在wrapper函数中,我们首先根据传入的参数生成 的key,并检查key是否已经存在于cache_dict中。如果key已经存在,说明该函数已经执行过,我们直接返回缓存的结果。如果key不存在,说明该函数没有执行过,我们执行原函数,并将结果存入到cache_dict中,以备下次使用。

3.3 计时

在函数执行前后记录一些时间信息,以便分析函数的性能瓶颈和优化点。比如我们可以定义一个timeit装饰器,如下所示:

import time

def timeit(func):
    def wrapper(*args, **kwargs):
        start = time.time()
        result = func(*args, **kwargs)
        end = time.time()
        print(f"[TIME] {func.__name__}() is executed in {end - start:.6f}s")
        return result
    return wrapper

@timeit
def sleep_and_print(n):
    time.sleep(n)
    print("hello world")

sleep_and_print(1)

在上面的代码中,我们定义了一个timeit装饰器,在wrapper函数中,我们使用Python内置的time模块获取函数执行前后的时间,以计算函数的执行时间。然后我们将结果输出到控制台,并返回函数的执行结果。

4. 结语

Python装饰器是Python语言中的重要特性,在开发过程中经常用到。通过应用装饰器,我们可以非常方便地给函数添加新的功能,比如记录日志、缓存结果、计时等等。此外,Python装饰器还可以帮助我们实现一些高级的功能,比如实现面向切面编程、实现ORM框架等等。在开发过程中,我们应该根据实际需求选择合适的装饰器来给函数添加附加功能,以提高代码的重用性、可维护性和可读性。