python 装饰器 decorators

python 装饰器 decorators

https://realpython.com/primer-on-python-decorators/

装饰器解释

术语对照表:https://docs.python.org/zh-cn/3/glossary.html#term-decorator

decorator – 装饰器

返回值为另一个函数的函数,通常使用 @wrapper 语法形式来进行函数变换。 装饰器的常见例子包括 classmethod()staticmethod()

装饰器语法只是一种语法糖,以下两个函数定义在语义上完全等价:

1
2
3
4
5
6
7
def f(arg):
...
f = staticmethod(f)

@staticmethod
def f(arg):
...

同样的概念也适用于类,但通常较少这样使用。有关装饰器的详情可参见 函数定义类定义 的文档。

chatgpt解释

Python装饰器是一种可以在不修改函数定义的情况下为函数添加额外功能的方法。装饰器是一种特殊类型的函数,它接收一个函数作为参数并返回一个新函数,新函数在原函数的基础上添加了额外的功能。装饰器的语法在 Python 中是通过 @ 语法实现的,例如:

1
2
3
@decorator
def function():
pass

装饰器可以用来给函数加入缓存、检查函数参数类型、记录函数调用日志等等。

装饰器详解

b站:【python】装饰器超详细教学,用尽毕生所学给你解释清楚,以后再也不迷茫了!

python里所有东西都是对象,函数也是对象。

  1. 因此函数也可以当作参数、变量(理解成是保存着函数对象的变量),传进其它函数里。

举例:

1
2
3
4
5
6
7
8
9
10
11
def double(x):
return x * 2

def triple(x):
return x * 3

def calc_number(func, x):
print(func(x))

calc_number(double, 3)
calc_number(triple, 3)
  1. 函数还可以成为一个返回值(函数的返回值可以是一个函数)

举例:

1
2
3
4
5
6
7
8
9
10
11
12
def get_multiple_func(n):

def multiple(x):
return n * x

return multiple

double = get_multiple_func(2) # 这里double变量其实拿到的是multiple这个函数对象,可以把double看作是multiple函数,只不过函数里面的n是2
triple = get_multiple_func(3)

print(double(3))
print(triple(3))

函数可被调用的,callable。装饰器是一个callable。

decorator是一个函数,写法是@后面跟着的名字,就是函数名。

1
2
3
4
5
6
7
8
9
def dec(f):
pass

@dec
def double(x):
return x * 2

# 完全等价
double = dec(double)

多练习几遍这个写法加深记忆! double函数上面写一个@dec函数,其实完全等价于这样写:double = dec(double),输入是double函数,dec的输出传给double变量(函数)

decorator是一个输入和输出都是函数的函数(其实输出不一定是函数,极端情况故意不是函数(没意义),一般写法都是函数的)

举例:(这里先不用看args和kwargs)

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
import time

def timeit(f):

def wrapper(x): # 参数只有一个,x
start = time.time()
ret = f(x)
print(time.time() - start)
return ret

return wrapper

@timeit
def my_func(x):
time.sleep(x)


@timeit
def other_func(x):
return x * 2


my_func(1)

print(other_func(2))

等价于 my_func = timeit(my_func) , 所以就是wrapper(1)传给左边my_func这个变量。

timeit函数当作decorator来用,输入f是一个函数,返回wrapper还是一个函数。

使用decorator好处:有不同函数时,都可以比较简洁的来完成这件事。

上述wrapper函数的初始形态为(补上args和kwargs):

1
2
3
4
5
6
7
8
9
def timeit(f):

def wrapper(*args, **kwargs):
start = time.time()
ret = f(*args, **kwargs)
print(time.time() - start)
return ret

return wrapper

args、kwargs作用:允许变长的函数参数 ,因此不管装饰的是什么函数,都能用这个装饰器。

还是以上面的例子为例,比如:

1
2
3
4
5
@timeit
def add(x, y):
return x + y

print(add(2,3))

对于两个参数,装饰器timeit也能用。

带参数的decorator:相当于在等价的过程中,多了一次函数调用。

1
2
3
4
5
6
@timeit(10)
def double(x):
return x * 2

# 完全等价
double = timeit(10)(double)

先计算timeit(10),返回一个函数,再用这个函数去调用double(传入double),再返回一个函数。

(之前不带参数的情况是:timeit(double),直接用timeit这个函数去调用double。)

举例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
def timeit(iteration):

def inner(f):

def wrapper(*args, **kwargs):
start = time.time()
for _ in range(iteration):
ret = f(*args, **kwargs)
print(time.time() - start)
return ret
return wrapper

return inner

@timeit(1000)
def double(x):
return x * 2

double(2)

# 完全等价
# double = timeit(1000)(double)
inner = timeit(1000)
double = inner(double)