Python函数装饰器Decorater()

本文主要讲述在Python中函数的一些用法, 仅仅作为笔记记录

本文主要参考自:

Python进阶


Python 中函数作为参数传递与返回

​在Python中,无论何时,我们都应当牢记一点:万物都为对象。因此,在Python中,函数本身也是一个对象,可以被赋值,甚至进行值拷贝,例如下面一段代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# 定义一个测试函数
def test_a():
print("This is a")
return
# 该测试函数赋值给a这个变量,再赋值给b这个变量
a = test_a
b = a
# 删除a这个变量,此时再运行a(),程序会出错, 但是b()仍然可以调用
del a
b()
# 删除 test_a() 这个函数, 再调用test_a() 时,程序会报错
del test_a
# 此时b()仍然可以调用
b()
# 会报错哦
test_a()

由此我们可以看见,在Python中, 函数本身也是一个可以作为值传递的变量。

故而,下面的这段代码就将一个函数作为参数传递进另一个函数中:

1
2
3
4
5
def func_in(func):
print("a")
func()
print("b")
return

那么显然我们可以看见这个程序在执行func()函数之前,一定会先执行一个print, 在执行之后也会再print。

不仅如此,在Python中,函数还可以被作为return的值返回,其原因与之前的类似。在此给出一个示例代码。

1
2
3
4
5
6
7
def func_return(func):
def returned(func):
func()
print("This is a function tht is be returned")
return returned
def a():
print("This is a")

因此,当我们运行一下代码后,会得到相应的结果:

1
2
3
4
5
6
a()
# This is a
b = func_return(a())
b()
# This is a
# This is a function tht is be returned

装饰器到底是什么

以上的代码中, 我们把函数作为参数传递,可以帮助我们在不破坏函数内部逻辑的情况下,让函数前后进行我们想要的附加操作。其实Decorater()装饰器, 做得也是一样的工作。

先让我们来看一下以下的代码:

1
2
3
4
5
6
7
8
9
10
def a_new_decoarter(func):
def wrapTheFuntion():
print("I'm doing som boring works before running my function")
func()
print("After")
return wrapTheFuntion
def a():
print("Funciton A is Running")
a = a_new_decoarter(a)
a()

有了我们之前的经验,我们可以自然而然的得出,在我们运行到a()时,我们最终会输出三行字符串, 为:

1
2
3
I'm doing som boring works before running my function
Funciton A is Running
After

以上就是Decorater的作用,装饰器的本质就是将函数本身的功能进行封装之后,再去修改函数前后的一些工作。

装饰器可以方便的修改替换函数的内部逻辑,而不去破坏外部的一些必要的处理。


装饰器的写法

以上的代码中并没有使用装饰器,但是简单直接地将装饰器的功能展示了出来。

下面就是把以上的代码转换为装饰器的过程

1
2
3
4
5
6
7
8
9
10
11
12
def a_new_decoarter(func):
def wrapTheFuntion():
print("I'm doing som boring works before running my function")
func()
print("After")
return wrapTheFuntion

@a_new_decoarter
def b():
print("Function B is Running")
# 这里使用了b()这个函数,来和上面的代码进行区别
b()

最终的运行结果如下:

1
2
3
I'm doing som boring works before running my function
Function B is Running
After

可以看见 @a_new_decorater 这个语句快速的将上面的b()的前后自动用wrapTheFunciton函数进行了封装。

其效果等效于之前示例中的 a = a_new_decorater(a())


使用装饰器后的函数名称

你可能会关心,在我使用了装饰器之后,我的函数名称,即b.__name__还是否仍然是b,在运行print(b.__name__)后我们可以得知,此时的b()的名称已经更改为wrapTheFunction()这个函数名称,这并不是我们想要的。因为我们想要的是装饰这个函数,但是不希望改变这个函数的名称。

于是我们可以使用functiontools包中的wraps模块。以下是使用的规范:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#  则可以使用如下的代码
from functools import wraps

def a_new_decorator(a_func):
# 利用wraps 先包装a_func 函数,再利用 decorater 函数进行包装
@wraps(a_func)
# 剩下的函数逻辑都是一样的
def wrapTheFunction():
print("I am doing some boring work before executing a_func()")
a_func()
print("I am doing some boring work after executing a_func()")
return wrapTheFunction

@a_new_decorator
def a_function_requiring_decoration():
print("I am the function which needs some decoration to "
"remove my foul smell")
a_function_requiring_decoration()
print(a_function_requiring_decoration.__name__)
# Output: a_function_requiring_decoration

由此,我们就了解了装饰器的一般使用方法


一个简单的装饰器应用

这里是我自己编写的关于计算一个排序算法的运行时间的装饰器:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
import time
from functools import wraps

# 计算时间的装饰器函数
def time_calculator_decorater(func):
@wraps(func)
def calculate_time(arr):
start = time.time()
func(arr)
elapsed = time.time() - start
print("Time is:", elapsed)
return calculate_time
# 快速排序辅助函数
def partition(arr:List[int], low:int, high:int)->None:
i = low
pivot = arr[i]
for j in range(low, high):
if arr[j] < pivot:
i += 1
arr[i], arr[j] = arr[j],arr[i]
arr[i], arr[low] = arr[low], arr[i]
return i
def QuickSort(arr:List[int], low:int, high:int)->None:
if low >= high-1:
return
else:
key = partition(arr, low, high)
QuickSort(arr, low, key)
QuickSort(arr, key+1, high)
# 最终的快速排序函数
@time_calculator_decorater
def QuickSortAll(arr):
QuickSort(arr, 0, len(arr)-1)

QuicSorAll(testArr[:1000])
# 运行结果为:Time is: 0.0010006427764892578

关于testArr的生成,采用random库进行了随机生成:

1
2
3
4
MAX_NUM = 100000
testArr = [0]*MAX_NUM
for i in range(MAX_NUM):
testArr[i] = random.randint(0,100000)

Tips

文章中提到的只是装饰函数的一小部分作用,更多更厉害的操作还请参看文首所提到的Python进阶