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

Python自定义函数中的高级用法:装饰器

发布时间:2023-06-16 02:01:27

装饰器是Python中的一个高级用法,它允许我们在不改变原有函数代码的情况下增加、扩展或修改函数的功能。本文将介绍装饰器的基本语法、作用以及实际应用。

1. 基本语法

装饰器本质上是一个函数,它接受一个函数作为参数,并返回一个新的函数。示例如下:

def decorator(func):
    def wrapper(*args, **kwargs):
        print("装饰器开始执行")
        result = func(*args, **kwargs)
        print("装饰器执行完毕")
        return result
    return wrapper

@decorator
def my_func():
    print("原函数执行")

my_func()

上述代码中,我们定义了一个装饰器函数decorator,它接受一个函数func作为参数,并返回一个新的函数wrapper。在wrapper函数中,我们首先打印一条装饰器开始执行的消息,然后调用原有函数func,并将其返回值存储在变量result中。最后,我们再打印一条装饰器执行完毕的消息,并将result返回。

接下来,在my_func函数定义前面加上@decorator,即可将my_func函数作为参数传递给decorator函数,并将decorator返回的新函数绑定到my_func上。当我们调用my_func函数时,其实调用的是wrapper函数,而wrapper函数会先打印一条装饰器开始执行的消息,然后调用原有的my_func函数,最后打印一条装饰器执行完毕的消息,并将my_func函数的返回值返回。因此,最终的输出结果为:

装饰器开始执行
原函数执行
装饰器执行完毕

2. 作用

装饰器的作用有很多,下面我们分别介绍。

2.1 扩展函数功能

通过装饰器,我们可以在不改变原有函数代码的情况下,为函数增加新的功能。例如,我们可以为函数加上计时、日志等功能,代码示例如下:

import time

def timeit(func):
    def wrapper(*args, **kwargs):
        start = time.time()
        result = func(*args, **kwargs)
        end = time.time()
        print(f"{func.__name__}运行耗时:{end - start}s")
        return result
    return wrapper

@timeit
def my_func():
    time.sleep(2)
    print("原函数执行")

my_func()

上述代码中,我们定义了一个计时装饰器timeit,它会在原有函数执行前后记录时间,并将耗时打印出来。在my_func函数定义前面加上@timeit,即可为my_func函数加上计时功能。当我们运行my_func函数时,其实会先调用timeit装饰器函数,创建一个新的函数wrapper,然后将my_func函数作为参数传递给wrapper函数,并将wrapper函数返回。当我们调用my_func函数时,其实是调用了wrapper函数,而wrapper函数会先记录当前时间,然后调用原有的my_func函数,最后记录结束时间,并打印出时间差。因此,最终的输出结果为:

原函数执行
my_func运行耗时:2.002129077911377s

2.2 控制函数执行次数

装饰器还可以控制函数执行次数,例如,我们可以定义一个装饰器once,让函数只能执行一次,代码示例如下:

def once(func):
    def wrapper(*args, **kwargs):
        if not wrapper.has_run:
            wrapper.has_run = True
            return func(*args, **kwargs)
        else:
            return f"{func.__name__}函数已经执行过了"
    wrapper.has_run = False
    return wrapper

@once
def my_func():
    print("原函数执行")

print(my_func())
print(my_func())

上述代码中,我们定义了一个只能执行一次的装饰器once,它会在 次执行时设定一个标志has_run,之后每次调用时都检查标志是否已经设定,如果已经设定则不再执行原有函数,而是返回一条提示信息。在my_func函数定义前面加上@once,即可让my_func函数只能执行一次。当我们调用my_func函数时,首先会调用once装饰器函数,创建一个新的函数wrapper,然后将my_func函数作为参数传递给wrapper函数,并将wrapper函数返回。当我们 次调用my_func函数时,其实是调用了wrapper函数,而wrapper函数会先检查标志has_run是否已经设定,如果还没有,则设定标志,并调用原有的my_func函数,最后返回原有函数的返回值。当我们第二次调用my_func函数时,由于标志已经设定过了,因此不再执行原有函数,而是返回一条提示信息。因此,最终的输出结果为:

原函数执行
my_func函数已经执行过了

2.3 检查函数参数

装饰器还可以用于检查函数参数,例如,我们可以定义一个装饰器check_args,用于检查函数参数是否合法,代码示例如下:

def check_args(*types):
    def decorator(func):
        def wrapper(*args):
            if len(args) != len(types):
                raise TypeError(f"{func.__name__}函数参数个数不匹配")
            for arg, t in zip(args, types):
                if not isinstance(arg, t):
                    raise TypeError(f"{func.__name__}函数参数类型不匹配")
            return func(*args)
        return wrapper
    return decorator

@check_args(str, int)
def my_func(name, age):
    print(f"姓名:{name},年龄:{age}")

my_func("张三", 18)

上述代码中,我们定义了一个检查函数参数的装饰器check_args,它接受一个或多个检查类型作为参数,并返回一个装饰器函数decorator。在decorator函数中,我们再次定义一个新的函数wrapper,用于检查函数参数是否与检查类型匹配。如果参数个数不匹配,或者某个参数的类型不匹配,则抛出TypeError异常。如果检查通过,则调用原有的func函数,并将其返回值返回。在my_func函数定义前面加上@check_args(str, int),即可为my_func函数增加检查参数类型的功能。当我们调用my_func函数时,首先会调用check_args装饰器函数,创建一个新的函数wrapper,然后将my_func函数作为参数传递给wrapper函数,并将wrapper函数返回。当我们调用my_func函数时,其实是调用了wrapper函数,而wrapper函数会先检查参数类型是否匹配,如果匹配则调用原有的my_func函数,最后返回原有函数的返回值。因此,最终的输出结果为:

姓名:张三,年龄:18

3. 实际应用

装饰器的实际应用非常广泛,下面我们介绍几个实际场景。

3.1 记录函数调用日志

我们可以定义一个装饰器log,用于记录函数的调用日志,代码示例如下:

`

def log(func):