装饰器的概念
在讲解装饰器之前,先看一段简单的程序,如下所示。
1 def f2(func):
2 def f1():
3 x = func()
4 return x + 1
5 return f1
6 def func():
7 print('func()函数')
8 return 1
9 decorated = f2(func)
10 print(decorated())
11 print(func())
运行结果如下图所示。
在上例中, 第1行定义了一个带单个参数func的名称为 f2的函数,第2行f1()函数为闭包,其中调用了func()函数并将func()函数的返回值加1返回。这样每次f2()函数被调用时,func的值可能会不同,但不论func()代表何种函数,程序都将调用它。
从程序运行结果可看出,调用函数decorated()的返回值为2,调用func()函数的返回值为1,两者都输出“func()函数”,此时称变量decorated是func的装饰版,即在func()函数的基础上增加新功能,上例是将func()函数的返回值加1。
大家可以用装饰版来代替func,这样每次调用时就总能得到
附带其他功能
的 func 版本,如下所示。
1 def f2(func):
2 def f1():
3 return func() + 1
4 return f1
5 def func():
6 print('func()函数')
7 return 1
8 func = f2(func)
9 print(func())
运行结果如下图所示。
运行结果
在上述代码中,第3行等价于上例中第3、4行,第8行将上例中第9行decorated改为func,这样每次通过函数名func调用函数时,都将执行装饰的版本。
通过上述代码可以得出装饰器的概念,即一个以函数作为参数并返回一个替换函数的可执行函数。装饰器的本质是一个嵌套函数,外层函数的参数是被修饰的函数,内层函数是一个闭包并在其中增加新功能。
@符号的应用
上例中使用变量名将装饰器函数与被装饰函数联系起来,此外,还可以通过@符号和装饰器名实现两者的联系,如下所示。
1 def f2(func):
2 def f1():
3 return func() + 1
4 return f1
5 @f2
6 def func():
7 print('func()函数')
8 return 1
9 print(func())
运行结果如下图所示。
运行结果
在上例中,第5行通过@符号和装饰器名实现装饰器函数与被装饰函数联系,第9行调用func()函数时,程序会自动调用装饰器函数的代码。
装饰有参数的函数
装饰器除了可以装饰无参数的函数外,还可以装饰有参数的函数,如下所示。
1 def f2(func):
2 def f1(a = 0, b = 0):
3 return func(a, b) + 1
4 return f1
5 @f2
6 def func(a = 0, b = 0):
7 print('func()函数')
8 return a + b
9 print(func(6, 8))
运行结果如下图所示。
运行结果
在上例中,第6行定义一个带有两个默认参数的func()函数,第5行将f2()函数声明为装饰器函数,用来修饰func()函数,第9行调用func装饰器函数,注意f1()函数中的参数必须包含对应func()函数的参数。
带参数的装饰器
通过上面的学习,装饰器本身也是一个函数,即装饰器本身也可以带参数,此时装饰器需要再多一层内嵌函数,如下所示。
1 def f3(arg = '装饰器的参数'):
2 def f2(func):
3 def f1():
4 print(arg)
5 return func() + 1
6 return f1
7 return f2
8 @f3('带参数的装饰器')
9 def func():
10 print('func()函数')
11 return 1
12 print(func())
运行结果如下图所示。
运行结果
在上例中,第1行定义装饰器函数,其由三个函数嵌套而成,最外层函数有一个装饰器自带的参数,内层函数不变,相当于闭包的嵌套。第8行将f3()函数声明为装饰器函数,用来修饰func()函数。
若读者不理解此代码,可以将装饰器写成如下代码,如下所示。
1 def f3(arg = '装饰器的参数'):
2 def f2(func):
3 def f1():
4 print(arg)
5 return func() + 1
6 return f1
7 return f2
8 def func():
9 print('func()函数')
10 return 1
11 f2 = f3('带参数的装饰器')
12 func = f2(func)
13 print(func())
运行结果如下图所示。
运行结果
在上例中,将装饰器分解成闭包的嵌套,这种写法更容易理解,此外,还可以将第11、12行代码写成如下代码:
func = f3('带参数的装饰器')(func)
上述代码相当于省略中间变量f2。
偏函数
函数最重要的一个功能的是复用代码,有时在复用已有函数时,可能需要固定其中的部分参数,除了可以通过默认值参数来实现之外,还可以使用偏函数,如下所示。
1 def myAdd1(a, b, c):
2 return a + b + c
3 def myAdd2(a, b):
4 return myAdd1(a, b, 123)
5 print(myAdd2(1, 1))
运行结果如下图所示。
运行结果
在上例中,第3行定义一个myAdd2()函数,与第1行myAdd1()函数的区别仅在于参数c固定为一个数字123,这时就可以使用偏函数的技术来复用上面的函数。
网友评论