装饰器与偏函数

2019-03-04  本文已影响20人  5f2a6061653d

装饰器的概念

在讲解装饰器之前,先看一段简单的程序,如下所示。

 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,这时就可以使用偏函数的技术来复用上面的函数。

上一篇 下一篇

猜你喜欢

热点阅读