Python 装饰器

不带参数的装饰器

核心思想就是设计模式中的装饰者模式。

按照 Java 中的写法,我们一般是这么做的:

定义一个专门处理日志打印的函数,接受需要处理的函数:

1
2
3
def print_log(func):
print 'call '+f.func_name+'()...'
func()

使用:
print_log(func)

其实在我( Java 程序员眼中)看来,这已经是非常的方便了,而且也没有破坏原函数的结构。

但是,对于 Python 来说还远远不够,它追求的是依然运行 func() 并且在不改变其内部结构的基础上扩展别的功能。

且 Python 还用语法糖简化了其使用过程。不过首先,也是非常关键的一点是要理解闭包的概念,上一篇的 Python 高级特性中介绍有提过。

用代码说话,下面就是一个简单的装饰器:

1
2
3
4
5
def log(f):
def fn(x):
print 'call ' + f.__name__ + '()...'
return f(x)
return fn

第一眼看真的是一脸懵逼,我花了大量的时间想弄清楚 return f(x) 到底是什么意思,有什么用,这个参数 x 又是什么意思。

于是我改造了一下,去掉了那个 returnx ,写成非常好理解的形式:

1
2
3
4
5
6
7
8
9
10
11
def log(f):
def fn():
print 'call ' + f.__name__ + '()...'
f()
return fn

def funcc():
print 'i am funcc'

funcc = log(funcc)
funcc()

运行:

call funcc()...
i am funcc

并没有问题。函数 log() 的用意是接收一个函数,在不改变传进来的函数的情况下,打印该函数的信息。

print 'call ' + f.__name__ + '()...'

这行就是打印原函数的名称,也就是装饰器的装饰物。

最后就是赋值给原先的函数。

上面那个只是没有参数的函数的装饰,对于有参数的函数,我们需要这么写:

1
2
3
4
5
6
7
8
9
10
11
def log(f):
def fn(*args, **kw):
print 'call ' + f.__name__ + '()...'
f(*args, **kw)
return fn

def funcc():
print 'i am funcc'

funcc = log(funcc)
funcc()

这个其实就是比较标准的 Python 装饰器了,除了一个地方,就是那个 return 。暂且我就当成是为了规范吧,因为对于函数 fn 来说好像通过 return 来获取传进来的函数 f 确实是没有用的啊。

上面那个代码,我运行之后发现,没有 return 是行不通的,所以这个 return 还是必须要加上去的,那为什么没有参数的就行的通呢

我好像突然想明白了 = =

理解了上面的代码之后,关于装饰器的语法糖就很好理解了,每次写:

1
2
funcc = log(funcc)
funcc()

肯定是觉得有点烦人的,所以 Python 提供了便捷方式:

要被装饰的函数可以这么写:

1
2
3
@log
def funcc():
print 'i am funcc'

之后使用就是直接:funcc() 就好了。

这个 @ 后面跟的就是之前自己定义的装饰器的函数名称。

带参数的装饰器

这里说的参数指的是装饰器上面的参数,不是装饰器接受的函数是否带参数。

使用带参数的装饰器就是在外面再包一层:

1
2
3
4
5
6
7
def log(text):
def decorator(func):
def wrapper(*args, **kw):
print('%s %s():' % (text, func.__name__))
return func(*args, **kw)
return wrapper
return decorator

那个 text 就是装饰器的参数,可以根据它来做增加一些业务逻辑,用法:

1
@log('参数')

类装饰器

相比函数装饰器,类装饰器更强大。。。

之后再补充吧,要炸了。