What is a Decorator?
参考Decorator Pattern wiki,(这个维基词条多讲得是Java/C#/C++相关,但是与python中的实现的功能类似.)
A decorator is the name used for a software design pattern. Decorators dynamically alter the functionality of a function, method, or class without having to directly use subclasses or change the source code of the function being decorated.
修饰模式,是面向对象编程领域中,一种动态地往一个类中添加新的行为的设计模式。就功能而言,修饰模式相比生成子类更为灵活,这样可以给某个对象而不是整个类添加一些功能。
通过使用修饰模式,可以在运行时扩充一个类的功能。原理是:增加一个修饰类包裹原来的类,包裹的方式一般是通过在将原来的对象作为修饰类的构造函数的参数。装饰类实现新的功能,但是,在不需要用到新功能的地方,它可以直接调用原来的类中的方法。修饰类必须和原来的类有相同的接口。
A decorator is any callable Python object that is used to modify a function, method or class definition. A decorator is passed the original object being defined and returns a modified object, which is then bound to the name in the definition. Python decorators were inspired in part by Java annotations, and have a similar syntax; the decorator syntax is pure syntactic sugar, using
@
as the keyword:
在python中,Decorator与函数式编程(Functional Programming)和元编程(Metaprogramming)息息相关,可以用Decorator来修改一个函数,类以及类的定义.
动机
接下来举几个简单例子说明python中为何要使用Decorator.
更加简明,易读
如下面两个例子,分别要实现一个classmethod方法和测试函数试行时间的功能,但是函数的定义和最后要实现的功能处出现了分离,对函数的操作变换代码出现在了函数的后面位置,不管是代码复杂性和逻辑都有问题.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16from time import time
# example 1: a classmethod function
def foo(self):
do_something
foo = classmethod(foo)
# example 2: timer for a function
def foo():
do_something
def timer(func):
def decor_fun():
start_time = time()
func()
print("Cost time: ", time()-start_time)
return decor_fun
foo = timer(foo)通过Decorator更加直观,明了
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17from time import time
# example 1
def foo(cls):
do_something
# example 2
def timer(func):
def decor_fun():
start_time = time()
func()
print("Cost time: ", time()-start_time)
return decor_fun
def foo():
do_something实现特定的功能
参考这个知乎问题:Python 中的 classmethod 和 staticmethod 有什么具体用途?,还可以结合函数式编程,某个函数功能需要被其他多个函数重复实现时,可以考虑Decorator,如: example 2测试函数运行时间, 输出log日志等等.
如何使用
通过example 2以及python Decorator Wiki不难看出@
符号其实是以要定义的函数foo
作为参数传入@
后面的函数bar
中,返回一个新的同时实现两者功能的函数.
1 |
|
带参数呢?
如果接触过函数式编程的话,不难解决这个问题,更普遍的方法可以使用*args
和**kwargs
关键字,针对带有默认参数和不带有默认参数的函数都可以解决.
1 | from time import time |
另外,Decorator也是可以输入参数的,还是上一个例子,实现调用n次函数.
1 | def ntimes(n): |
本例中,从函数式编程角度看,深度更深了一层,从@
本质理解,其后符号ntimes也是函数,故也可接受参数,一般Decorator不会比这个更深,所以了解即可. 详情可以了解python中的闭包.
多个Decorator
道理都是类似的,语法为
1 |
|
Class Decorator
In Python prior to version 2.6, decorators apply to functions and methods, but not to classes. Decorating a (dummy)
__new__
method can modify a class, however.[25]Class decorators are supported[26] starting with Python 2.6.
Python 2.6之后才支持class decorators,之前都是通过metaclass来实现相应功能,__new__
方法修改类.
按照之前的解释,Class Decorator与Function Decorator应该是基本相同的,只是装饰的对象不同而已.
Decorator 作用整个类
可以看到此时的decorator接受一个类作为函数参数,返回一个修饰之后的类,跟之前一样的道理.
1 | def decorator(cls): |
Decorator本身是类
如下面例子所示,
__init__
方法接受要修饰的函数为参数.
__call__
方法在调用被修饰函数时调用.
此外还有__set__
, __get__
, __getattribute__
等方法均可修改.
1 | class decorator(object): |
从这个角度看,Decorator可以实现修改调用模型的这一功能. 可以参考修改调用模型.
改进
仔细研读细节就会发现,使用decorator可以完成期望的功能,取得成效,但是也会带来代码细节与原本出现偏差的问题,毕竟对了一层调用,改进方法可以使用 functools
的wraps
Decorator.
1 | from functools import wraps |
也可以使用decorator.
1 | from decorator import decorator |