Python 装饰器
装饰器背后的主要动机源自 python 面向对象编程。装饰器是在函数调用之上的修饰。这些修饰仅是当声明一个函数或者方法的时候,才会应用的额外调用。
装饰器的语法以@开头,接着是装饰器函数的名字和可选的参数。紧跟着装饰器声明的是被修饰
的函数,和装饰函数的可选参数。装饰器看起来会是这样:
@decorator(dec_opt_args)
def func2Bdecorated(func_opt_args):
pass
装饰器语法如何(以及为什么)产生的呢?
那还得从python2.2说起,当时静态方法和类方法实现方法很笨拙:
class MyClass(object):
def staticFoo():
staticFoo = staticmethod(staticFoo)
在上面MyClass这个类的声明中,我们定义了一个叫staticFoo()的方法。现在因为打算让它成为静态方法,省去它的 self 参数,self 参数在标准的类方法中是必需的。在 python2.2中要用 staticmethod()内建函数来将这个函数“转化“为静态方法,但是在 def staticFoo()后跟着 staticFoo = staticmethod(staticFoo)
显得有些臃肿。使用装饰器,你现在可以用如下代码替换掉上面的:
class MyClass(object):
@staticmethod
def staticFoo():
...
装饰器可以如函数调用一样“堆叠“起来,下面是使用了多个装饰器:
@deco2
@deco1
def func(arg1, arg2, ...):
pass
多个装饰器的叠加使用和函数的嵌套组合是等价的:
def func(arg1, arg2, ...):
pass
func = deco2(deco1(func))
函数组合用数学来定义就像这样: (g · f)(x) = g(f(x)),在python中用装饰器表示是这样的:
@g
@f
def foo():
pass
无参数和有参数的装饰器
无参数,的等价于foo = deco(foo)
@deco
def foo():
pass
带参数的装饰器 decomaker(), 等价于foo = decomaker(deco_args)(foo)
@decomaker(deco_args)
def foo(): pass
意思就是,decomaker()用 (参数)deco_args 做了些事并返回了一个函数对象(还是一个装饰器),而该函数对象以 foo 作为其参数,对 foo() 进行一些’装饰’后返回一个和foo同名的函数.
到底装饰器是什么?
装饰器实际就是函数,接受函数对象。装饰器仅仅是用来“装饰“(或者修饰)函数的包装,返回一个修改后的函数对象,将其重新赋值原来的标识符,并永久失去对原始函数对象的访问。
但它们是怎样处理那些函数的呢?
一般说来,当你包装一个函数的时候,你最终会调用它.最棒的是我们能在包装的环境下在合适的时机调用它。我们在执行函数之前,可以运行些预备代码,如post-morrem[尸检] 分析,也可以在执行代码之后做些清理工作。所以当你看见一个装饰器函数的时候,很可能在里面找到这样一些代码,它定义了某个函数并在定义内的某处嵌入了对目标函数的调用或者至少一些引用。从本质上看,这些特征引入了 java 开发者称呼之为 AOP(Aspect Oriented Programming,面向方面编程)的概念。
在装饰器中置入通用功能的代码来降低程序复杂度。对于用 python 创建企业级应用,支持装饰器的特性是非常重要的。例如,可以用装饰器来:
- 引入日志
- 增加计时逻辑来检测性能
- 给函数加入事务的能力
装饰器的例子
例子通过显示函数执行的时间"装饰"了一个函数。这是一个"时戳装饰"
#!/usr/bin/env python
from time import ctime, sleep
# 显示何时调用函数的时戳的装饰器
def tsfunc(func):
# tsfunc的内部/内嵌函数
def wrappedFunc():
print ('[%s] %s() called' % (ctime(), func.__name__))
return func()
return wrappedFunc
@tsfunc
def foo():
pass
#--------------------------------
for i in range(3):
sleep(2)
foo()
[Sun Nov 4 12:15:05 2018] foo() called
[Sun Nov 4 12:15:07 2018] foo() called
[Sun Nov 4 12:15:09 2018] foo() called