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

Python函数——装饰器及应用

发布时间:2023-06-11 05:54:12

装饰器是 Python 中一个非常强大的功能,它允许你改变现有的函数的行为。在不修改函数源代码的情况下,我们可以为函数添加新的行为或者改变函数的行为。

装饰器可以用于很多场景,比如缓存、计时、验证、日志等。下面我们将手把手学习 Python 装饰器及其应用。

1. 装饰器基础

Python 在定义函数时,使用 @ 符号可以快速的为函数添加一个装饰器。定义装饰器的方式非常灵活,可以接收函数对象作为参数,返回经过修饰后的函数对象。

下面我们定义一个装饰器,为任意函数添加计时功能:

import time

def time_it(func):
    def wrapper(*args, **kwargs):
        start = time.time()
        result = func(*args, **kwargs)
        end = time.time()
        print(f'{func.__name__} took {(end - start) * 1000} ms')
        return result
    return wrapper

在上面的代码中,time_it 函数接收一个函数对象作为参数,然后返回一个新的函数对象 wrapper。wrapper 函数的内部实现是先记录函数调用的开始时间,执行函数,记录函数调用结束时间,并计算函数调用耗费的时间。最后,wrapper 函数返回原始函数的执行结果。

为了让我们的装饰器生效,我们需要引用 @time_it 这个装饰器,将其添加到需要计时的函数上:

@time_it
def fibonacci(n):
    if n <= 1:
        return n
    else:
        return fibonacci(n-1) + fibonacci(n-2)

print(fibonacci(35))

运行结果如下所示:

fibonacci took 7653.529596328735 ms
9227465

我们可以看到,经过装饰器的修饰,fibonacci 函数的执行结果被输出,同时被计算了执行时间。

2. 装饰器链

Python 函数可以被多个装饰器装饰,多个装饰器将依次对函数进行修饰。例如,在函数上使用了两个装饰器:

@decorator1
@decorator2
def foo():
    pass

这样,decorator2 装饰器将首先应用于 foo 函数,然后 decorator1 装饰器将再次应用于 foo 函数。即装饰器的优先级是由上往下的。

3. 装饰器参数

装饰器可以在使用时接受参数。例如,我们可以定义一个参数为一个数字的装饰器:

def repeat(n):
    def decorator(func):
        def wrapper(*args, **kwargs):
            for i in range(n):
                func(*args, **kwargs)
        return wrapper
    return decorator

这个装饰器的作用是将函数执行重复 n 次。这里装饰器本身需要接收一个数字作为参数,同时还需要接收一个函数作为参数。

接下来,我们在函数上使用这个装饰器:

@repeat(3)
def foo():
    print('hello')

foo()

这里我们希望 foo 函数被执行 3 次,所以在 @ 符号后我们加上了参数 3。运行结果如下所示:

hello
hello
hello

我们可以看到,foo 函数被执行了 3 次。

4. 实际应用

装饰器在实际应用中非常方便。比如,我们可以使用装饰器记录函数的调用次数:

def count_calls(func):
    def wrapper(*args, **kwargs):
        wrapper.calls += 1
        return func(*args, **kwargs)
    wrapper.calls = 0
    return wrapper

这里,我们定义了 count_calls 装饰器,在 wrapper 函数内部增加了一个计数器来记录函数被调用的次数。装饰器的调用方式如下所示:

@count_calls
def foo():
    print('hello')

foo()
foo()
foo()

print(foo.calls)

这里,我们使用 @ 符号引用装饰器,并定义了一个 foo 函数。调用 foo 函数 3 次后,输出 foo 函数被调用的次数。运行结果如下所示:

hello
hello
hello
3

我们可以看到,装饰器非常灵活,可以用于不同的场景。在实际开发中,我们可以基于装饰器构建更为复杂的逻辑来提供更加便捷的工具。