介绍python的函数装饰器

1. 函数装饰器的概念

在Python中,函数是一等对象,可以赋值给变量、作为参数传递、作为返回值。同时,Python还支持一种高级特性,即函数装饰器(Function Decorator)。

所谓函数装饰器,就是在不改变函数源代码和调用方式的前提下,给函数增加新的功能。它是通过一个用@符号修饰的Python函数进行声明的,接受一个函数作为输入,并返回另一个函数作为输出。

def decorator_function(original_function):

def wrapper_function():

print("Wrapper function executed this before the original function!")

return original_function()

return wrapper_function

@decorator_function

def display():

print("Display function ran")

display()

运行这段代码,我们可以得到以下输出:

Wrapper function executed this before the original function!

Display function ran

我们分别来看看这个代码段的三个组成部分:

1.1 decorator_function

这个函数是一个装饰器函数,它接受一个名为original_function的函数为输入,并返回一个内部函数wrapper_function。它的作用是将original_function函数进行装饰。

1.2 wrapper_function

这个函数是一个闭包(Closure),它可以访问decorator_function函数的局部变量,把原函数original_function包含在其中,并在调用原函数前后加上自己的逻辑。在本例中,wrapper_function在调用原函数前输出一条文字信息,然后返回原函数的执行结果。

1.3 @decorator_function

这是使用装饰器的语法糖。它相当于执行了以下代码:

display = decorator_function(display)

即把名为display的函数作为参数传入decorator_function函数,并将decorator_function函数的返回值重新赋值给display。此时,display指向的是wrapper_function函数。

2. 应用实例:计算函数运行时间

下面我们来看一个使用装饰器计算函数执行时间的例子。这个装饰器接受一个函数为输入,输出另一个函数,这个输出函数相当于原函数加上计算运行时间的功能。代码如下:

import time

def time_it(func):

def wrapper(*args, **kwargs):

start_time = time.time()

result = func(*args, **kwargs)

end_time = time.time()

print(f"Function {func.__name__} executed in {end_time - start_time:.12f}s")

return result

return wrapper

@time_it

def count_up_to(number):

total = 0

for i in range(1, number+1):

total += i

return total

print(count_up_to(1000000))

输出:

Function count_up_to executed in 0.026266813278s

500000500000

我们用time_it装饰了count_up_to函数,并执行它。我们看到输出了计算时间和函数的返回值。

这个例子中,我们在wrapper函数内部调用了func函数,并计算了它的执行时间。最后输出了这个执行时间,并返回了原来的函数结果。

3. 装饰器的有用技巧

3.1 验证函数参数

我们可以通过装饰器来验证函数的参数类型、数量和取值范围等。以下代码演示了验证函数参数是否为数字的方法。

def require_number(func):

def wrapper(*args, **kwargs):

for arg in args:

if type(arg) not in [int, float]:

raise ValueError("Argument must be a number!")

return func(*args, **kwargs)

return wrapper

@require_number

def multiply(num1, num2):

return num1 * num2

print(multiply(2, 3))

print(multiply("2", 3))

输出:

6

ValueError: Argument must be a number!

可以看到,我们先定义了一个装饰器函数require_number,它用于验证输入参数是否为数字。然后我们通过在multiply函数定义前添加@require_number,来对multiply函数进行装饰。

3.2 缓存函数结果

有些函数计算结果比较耗时。如果函数的输入相同,那么计算结果也相同。因此,我们可以使用装饰器来缓存函数的计算结果,避免重复计算。

def memoize(func):

cache = {}

def wrapper(*args):

if args not in cache:

cache[args] = func(*args)

return cache[args]

return wrapper

@memoize

def fibonacci(num):

if num in [0, 1]:

return num

else:

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

print(fibonacci(40))

输出:

102334155

我们定义了一个装饰器函数memoize,它使用字典cache来缓存函数的计算结果。当函数被调用时,我们首先检查函数的输入参数args是否已经在cache中有结果,如果有结果直接返回,否则调用原函数进行计算,并将计算结果加入到cache中。

我们用这个装饰器来装饰了递归计算斐波那契数列的函数fibonacci。由于斐波那契数列会逐渐增加计算量,所以我们计算了第40项,需要很长时间。但是如果我们多次调用这个函数来计算其他项目,则可以显著减少计算时间。

3.3 记录函数执行日志

我们可以把函数执行的日志记录下来,以便后续查看。以下代码演示了使用装饰器记录函数执行日志的方法。

def logger(func):

import logging

logging.basicConfig(filename=f"{func.__name__}.log", level=logging.INFO)

def wrapper(*args, **kwargs):

logging.info(

f"Ran with args: {args}, and kwargs: {kwargs}")

return func(*args, **kwargs)

return wrapper

@logger

def my_function(arg1, arg2):

print(arg1 + arg2)

my_function(4, 5)

运行这个代码,我们可以看到在程序所在目录下生成了一个my_function.log文件,里面记录了函数的执行日志:

INFO:root:Ran with args: (4, 5), and kwargs: {}

我们定义了一个装饰器函数logger,它使用Python标准库中的logging模块记录日志,并将日志输出到文件中。

以上就是函数装饰器的一些常见用法和技巧。在实际代码编写中,我们可以通过使用装饰器来提高代码的可读性、可重用性以及安全性等方面的优化。

后端开发标签