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

Python中的装饰器函数完全指南

发布时间:2023-06-12 07:41:02

Python中的装饰器函数是一个非常强大的功能,可以让我们在不改变函数本身的情况下,增加一些额外的功能。本文将全面介绍Python中的装饰器,包括其定义、用法、实现和常见的应用场景等。

1. 装饰器函数的定义

在Python中,装饰器函数是一个将函数作为参数并返回一个新函数的函数。它可以用来修改、增强或包装原始函数的功能。

装饰器函数的基本语法为:

def decorator_function(original_function):
    def new_function(*args, **kwargs):
        # 在新函数中增加额外的功能
        return original_function(*args, **kwargs)
    return new_function

其中,original_function是需要被增强功能的原始函数,new_function是一个新的函数,它会包装原始函数并增加一些额外的功能。

2. 装饰器函数的用法

使用装饰器函数可以给函数增加各种各样的功能,例如:

- 记录函数的运行时间;

- 给函数增加参数验证;

- 缓存函数的计算结果。

我们可以通过装饰器函数来实现这些功能,而不需要修改原始函数的代码。例如:

import time

def timer(original_function):
    def new_function(*args, **kwargs):
        start_time = time.time()
        result = original_function(*args, **kwargs)
        end_time = time.time()
        print(f"{original_function.__name__}运行时间:{end_time - start_time:.2f}s")
        return result
    return new_function

@timer
def my_function():
    time.sleep(2)

my_function()

上面的代码中,timer装饰器函数会记录被装饰函数的运行时间,并在函数运行结束后输出运行时间。使用@timer语法糖,就可以在不改变my_function函数的代码的情况下,为它增加一个计时器的功能。

3. 装饰器函数的实现

在Python中,装饰器函数本质上就是一个普通的Python函数。它可以使用所有Python函数支持的语法和特性,例如:

- 指定函数参数和返回值类型;

- 使用闭包存储某些状态等。

我们可以使用装饰器函数实现各种各样的装饰器。

3.1 同时使用多个装饰器

在Python中,可以同时使用多个装饰器来增强函数的功能。例如:

import time

def timer(original_function):
    def new_function(*args, **kwargs):
        start_time = time.time()
        result = original_function(*args, **kwargs)
        end_time = time.time()
        print(f"{original_function.__name__}运行时间:{end_time - start_time:.2f}s")
        return result
    return new_function

def logger(original_function):
    def new_function(*args, **kwargs):
        print(f"函数{original_function.__name__}被调用了,参数为:args={args}, kwargs={kwargs}")
        result = original_function(*args, **kwargs)
        print(f"函数{original_function.__name__}的执行结果为:{result}")
        return result
    return new_function

@logger
@timer
def my_function():
    time.sleep(2)

my_function()

上面的代码中,timer装饰器函数会记录被装饰函数的运行时间,logger装饰器函数会记录被装饰函数的调用参数和执行结果。使用@logger和@timer语法糖,就可以在不改变my_function函数的代码的情况下,为它增加两个装饰器的功能。

需要注意的是,使用多个装饰器时,装饰器的执行顺序是从下往上的,也就是先执行最后一个装饰器,然后依次向上执行。

3.2 使用类实现装饰器

在Python中,装饰器函数也可以使用类来实现,这通常情况下比使用函数实现更加灵活。例如:

import time

class Timer:
    def __init__(self, func):
        self.func = func

    def __call__(self, *args, **kwargs):
        start_time = time.time()
        result = self.func(*args, **kwargs)
        end_time = time.time()
        print(f"{self.func.__name__}运行时间:{end_time - start_time:.2f}s")
        return result

@Timer
def my_function():
    time.sleep(2)

my_function()

上面的代码中,Timer类实现了__init__和__call__两个方法,前者用来初始化实例,后者用来包装原始函数和增强它的功能。使用@Timer语法糖,就可以为my_function函数增加一个计时器的功能。

需要注意的是,使用类实现装饰器时,需要将类实例化后才能使用,例如:

timer = Timer(my_function)
timer()

4. 常见的装饰器应用场景

装饰器函数是Python中的一个非常强大的功能,它可以用于实现各种各样的功能。以下是一些常见的装饰器应用场景:

4.1 参数验证

在Python中,我们经常需要对函数的输入参数进行验证,以防止程序出错。使用装饰器可以很方便地实现这个功能。例如:

def check_input(original_function):
    def new_function(*args, **kwargs):
        for arg in args:
            if not isinstance(arg, int):
                raise ValueError("参数必须为整数!")
        return original_function(*args, **kwargs)
    return new_function

@check_input
def my_function(x, y):
    print(x + y)

my_function(1, 2)      # 正常调用,输出3
my_function("a", 2)    # 抛出异常

上面的代码中,check_input装饰器函数会检查my_function函数的输入参数是否为整数,如果不是,则抛出异常。

4.2 记录日志

在Python中,我们经常需要记录程序的运行日志,以便调试程序。使用装饰器可以很方便地实现这个功能。例如:

import logging

logging.basicConfig(level=logging.INFO, format='%(asctime)s %(levelname)s: %(message)s', datefmt='%Y-%m-%d %H:%M:%S')

def log_info(original_function):
    def new_function(*args, **kwargs):
        logging.info(f"函数{original_function.__name__}被调用了,参数为:args={args}, kwargs={kwargs}")
        result = original_function(*args, **kwargs)
        logging.info(f"函数{original_function.__name__}的执行结果为:{result}")
        return result
    return new_function

@log_info
def my_function(x, y):
    return x + y

my_function(1, 2)

上面的代码中,log_info装饰器函数会使用Python的logging模块记录my_function函数的调用参数和执行结果。

4.3 缓存函数计算结果

在Python中,我们经常需要计算一些耗时的函数,如果多次调用同一个函数,它的计算结果可能是相同的,这时我们可以使用缓存来避免重复计算。使用装饰器可以很方便地实现这个功能。例如:

`

import time

import functools

def cache(original_function):

cache_dict = {}

@functools.wraps(original_function)

def new_function(*args, **kwargs):

key = tuple(args) + tuple(sorted(kwargs.items()))

if key in cache_dict:

return cache_dict[key]

else:

result = original_function(*args, **kwargs)

cache_dict[key] = result

return result

return new_function

@cache

def fibonacci(n):

if n == 1 or n == 2:

return 1

else:

return fibonacci(n - 1) + fibonacci(n - 2)

start_time