python装饰器

装饰器的作用就是为函数添加额外的功能。

装饰器原理

< 函数+实参高阶函数+返回值高阶函数+嵌套函数+语法糖 = 装饰器 >

直接来看例子:

1
2
3
4
5
def x(fuc): # 参数为函数(实参高阶函数)
def y(): # 嵌套函数
fuc()
print("hai")
return y # 返回一个函数(返回值高阶函数)

其实,函数 x 就可以作为一个装饰器。

接下来用 x 来装饰另一个函数:

1
2
3
4
5
6
7
8
9
10
11
def x(fuc): # 参数为函数(实参高阶函数)
def y(): # 嵌套函数
fuc()
print("hai")
return y # 返回一个函数(返回值高阶函数)

def test(): # 被装饰函数
print('hello')

test = X(test)
test()

运行代码,结果依次输出 “hello”,”hi”。

test() 是被装饰函数,其实被装饰函数就是装饰器的参数。上面的代码可以等价于:

1
2
3
4
5
6
7
8
9
10
11
def x(fuc): # 参数为函数(实参高阶函数)
def y(): # 嵌套函数
fuc()
print("hai")
return y # 返回一个函数(返回值高阶函数)

@x # 语法糖
def test(): # 被装饰函数
print('hello')

test()

这两个程序的运行结果是一样的。@x 就是 test = x(test) 的语法糖。


被装饰函数有参数

那如果被装饰函数有参数,那怎么传到装饰器中呢?

我们知道,test = x(test),而 x 返回了函数 y,那么就有 test = y。所以 给 test 传参数的同时也给 y 传入参数就可以了,而参数 fuc 接收 test,因此 fuc 也要传入参数。为了使程序更加有扩展性,因此在装饰器中的 y() 和 func(),加如了可变参数 *args 和 **kwargs。

1
2
3
4
5
6
7
8
9
10
11
def x(fuc): # 参数为函数(实参高阶函数)
def y(*args, **kwargs): # 嵌套函数
fuc(*args, **kwargs)
print("hai")
return y # 返回一个函数(返回值高阶函数)

@x # 语法糖
def test(a, b, **c): # 被装饰函数
print(a, b, c)

test(1, 2, p=5)

结果输出:

1
2
1 2 {'p': 5}
hahaha

装饰函数有参数

有的装饰器带有参数,像这样:

1
@decorator(parameter = value)

但是装饰器传入的是函数,那么这个参数如何传到装饰器中呢?我们可以再加一层函数来接收这个参数:

1
2
3
4
5
6
7
8
9
10
11
12
def x(parameter): 
def f(fuc):
def y(*args, **kwargs):
fuc(*args, **kwargs)
print(parameter)
print("hai")
return y
return f

@x(parameter='')
def test(a, b, **c):
print(a, b, c)

关于更详细的介绍,请参考 Python装饰器的通俗理解


常见装饰器介绍

@classmethod

classmethod 修饰符对应的函数不需要实例化,不需要 self 参数,但第一个参数需要是表示自身类的 cls 参数,可以来调用类的属性,类的方法,实例化对象等。

1
2
3
4
5
6
7
8
9
10
11
class Test:
def __init__(self):
self.a = 1
self.b = 2

@classmethod
def add(cls):
return cls().a + cls().b

res = Test.add() # 不需要实例化可直接调用
print(res) # 输出 3
@staticmethod

staticmethod 修饰符对应的函数不需要实例化,不需要 self 参数,也不需要表示自身类的 cls 参数,就跟使用函数一样。

staticmethod 基本可以被 classmethod 代替。

@property

property 装饰器用来创建只读属性,它会将函数转换为相同名称的只读属性,这样可以防止属性被修改。

1
2
3
4
5
6
7
8
9
10
11
class Test:    
def __init__(self):
self.a = 1
self.b = 2

@property
def add(self):
return self.a + self.b

test = Test()
print(test.add) # 像调用变量一样