1. python中函数的特性

python中,一切皆对象,函数也不例外,故python中函数的以下4个核心特性也就不难理解了:

  • 函数可作为变量进行传递

  • 函数可作为入参进行传递

  • 函数可作为返回值进行传递

  • 嵌套函数可以跨域访问(闭包)

  1. def func():
  2. return '函数func'
  3. def outer(x): # 函数作为入参进行传递
  4. def inner():
  5. return '戴了inner帽子的' + x() # 嵌套函数inner跨域访问x
  6. return inner # 函数作为返回值进行传递
  7. closure = outer(func) # 函数作为变量进行传递
  8. print(func())
  9. print(closure())

2. python装饰器

第一部分的实例,即是装饰器的雏形了。
装饰器主要用于将 待装饰对象 进行一次外部封装,可以理解为在调用待装饰对象前后各安置一个钩子,在不改变 待装饰对象及其调用方式的前提下 进行封装。

2.1 装饰器必须要遵守的两原则

由以上描述,可以看出,装饰器必须要满足以下两个原则才能够满足需求:

  1. 不修改被装饰函数的源码;

  2. 不修改被装饰函数的调用方式。

以上两点并不难理解,在既有庞大代码中,如果无法满足上述两点中的任何一点,在使用时都将需要对现有代码做较大调整,这无疑是失败的设计。

2.2 函数装饰器实现

将以上例子进行稍微改造,即可满足装饰器遵守原则:

  1. def func():
  2. return '原型函数func,装饰器不应该影响我'
  3. def outer(x):
  4. def inner():
  5. print("我是装饰函数前置钩子")
  6. return x() # 满足原则1
  7. return inner
  8. In [3]: repr(func)
  9. Out[3]: '<function func at 0x6fffd3ef268>'
  10. func = outer(func) # 满足原则2
  11. print(func())
  12. 输出:
  13. 我是装饰函数前置钩子
  14. 原型函数func,装饰器不应该影响我
  15. In [5]: repr(func)
  16. Out[5]: '<function outer.<locals>.inner at 0x6fffd50cea0>'

将原型func以入参形式传入outer;
outer内部的嵌套函数inner首先调用前置钩子(此处以简单的print代替),再调用原型函数func,并将其返回值传出,但此时并没有真正执行函数,因为 return x() 实际是定义在嵌套函数中,并未真正执行;
outer将嵌套函数inner作为函数对象传出,待真正调用时执行;
为了满足装饰器原则2,需要将outer返回的函数对象(inner)重命名为func,以便在现有源代码不变的前提下,将调用装饰函数inner修改为调用原函数func,但此时的func,已经非原型函数func了,可以从前后两次的 repr(func) 看出,对象已经发生改变。

经过以上实例,已经可以看到装饰器实现的原型及基本原理了。

2.3 装饰器的执行时机

当模块被load时,被装饰的函数即会执行装饰函数,成为 import time

@outer
def func()

实际上等价于 outer = outer(func)