Python decorator notes

基本定义

装饰器背后的主要动机源自Python面向对象编程。装饰品是在函数调用之上的装饰,装饰品语法以@开头,接着是装饰器函数的名字和可选的参数 ,紧跟着装饰器声明的是被修饰的函数和装饰函数的可选参数,看起来会是这样:

@decorator(dec_opt_args)
def func2Bdecorated(func_opt_args):
:

当静态方法和类方法在2.2时被加入到Python中的时候,实现方法很笨拙:

class MyClass(object):
def staticFoo():
:
staticFoo = staticmethod(staticFoo)

在这个类的声明中,定义了叫staticFoo()的方法,现在因为打算让它成为静态方法,省去了它的self参数,但self参数在标准的类方法中是必需的。接着用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():
:

foo=g(f(foo))相同

有参数和无参数的装饰器

没有参数的情况,一个装饰器如:

@deco
def foo():pass

非常地直接,相当于foo = foo(deco)
带参数的装饰器decomaker():

@decomaker(deco_args)
def foo():pass

需要自己返回以函数作为参数的装饰器。decomaker()deco_args做了些事并返回函数对象,而该函数对象正是以foo作为其参数的装饰器。

foo = decomaker(deco_args)(foo)

以下为含有多个装饰器的例子,其中的一个装饰器带有一个参数:

@deco1(deco_arg)
@deco2
def func(): pass

等价于
func = deco1(deco_arg)(deco2(func))

So What Are Decorators?

装饰器实际上就是函数。它们也接受函数对象,但是它们怎样处理那些函数呢?通常来讲,当你包装一个函数的时候,你最终会调用它。最棒的是我们能在包装的环境下在合适的时机调用它。我们在执行函数之前,可以运行些预备代码,例如post-morrem分析(算后检查),也可以在执行代码之后做些清理工作,取决于编程者。所以当你看到一个装饰器函数的时候,很可能在里面找到这样一些代码,它定义了某个函数并在定义内的某处嵌入了对目标函数的调用或者至少一些引用。从本质上看,这些特征引入了Java开发者称呼之为AOP(Aspect Oriented Programming,面向方面编程)的概念。

We know that decorators are really functions now. We also know that they take function objects. But what will they do with those functions? Generally, when you wrap a function, you eventually call it. The nice thing is that we can do that whenever it is appropriate for our wrapper. We can run some preliminary code before executing the function or some cleanup code afterward, like post- mortem analysis. It is up to the programmer. So when you see a decorator function, be prepared to find some code in it, and somewhere embedded within its definition, a call or at least some reference, to the target function.This feature essentially introduces the concept that Java developers call AOP, or aspect-oriented programming.

可以考虑在装饰器中置入能用功能的代码来降低程序复杂度,例如,可以用装饰器来:

  • 引入日志;
  • 增加计时逻辑来检测性能
  • 给函数加入事务的能力

Decorator Example

下例通过显示函数执行的时间“装饰”了一个(没有用的)函数,这是一个“时戳装饰”

#!/usr/bin/env python
from time import ctime, sleep
def tsfunc(func):
def wrappedFunc():
print '[%s] %s() called' % (
ctime(), func.__name__)
return func()
return wrappedFunc
@tsfunc
def foo():
pass
foo()
sleep(4)
for i in range(2):
sleep(1)
foo()

输出结果如下:

[Wed Sep 28 09:46:35 2016] foo() called
[Wed Sep 28 09:46:40 2016] foo() called
[Wed Sep 28 09:46:41 2016] foo() called

tsfunc()是一个何时调用函数的时戳的装饰器,它定义了一个内部的函数wrappeFunc(),该函数增加了时戳以及调用了目标函数。装饰器的返回值是一个“包装了”的函数。用空函数体(什么也不做)来定义了foo()函数并用tsfunc()来装饰。立刻调用它,然后等待4秒,然后再调用两次,并在每次调用前暂停1秒。结果函数立刻被调用,第1次调用后,调用函数的第2个时间点应该为5(4+1),第3次的时间应该大约为之后的1秒。