关于Python decorator的应用

最近继续给Ubuntu Tweak不断的添砖加瓦,目标将于下个月末与Ubuntu 12.04一齐发布一个新版本----Ubuntu Tweak 0.7。

在这个过程中,有了不少心得,也确实好久没有写具体的技术文章,今天就来写一篇吧~

今天要介绍的是Python的decorator的应用,我不打算介绍什么是decorator。因为学习一样技术不难,难的是如何把它应用起来。昨天我在说我在用Decorator,微博上有个人说decorator没啥用,于是我就介绍个我在用的案例。

在Ubuntu Tweak这个项目当中,我使用logging来进行全局的log输出。比如我经常会在一些函数中打印出这个函数的名字、参数等:

def load_modules(self, name, default="test"):
    log.debug("Loading modules...%s, %s" % (name, default))
    do_something()

这种方式用的多了,也就烦了。因为每个函数的名称不同,参数的个数与类型也不同,有没有更简单的方法来达到我的目的?于是我想到了decorator。我写了下面这么一个wrapper(decorator):

def log_func(log):
    def wrap(func):
        def func_wrapper(*args, **kwargs):
            log.debug("%s:" % func)
            for i, arg in enumerate(args):
                log.debug("\targs-%d: %s" % (i + 1, arg))
            for k, v in enumerate(kwargs):
                log.debug("\tdict args: %s: %s" % (k, v))
            return func(*args, **kwargs)
        return func_wrapper
    return wrap

这个decorator是什么意思呢?

很简单,它接受一个log的参数,即传入logger对象。然后它会先把函数名打印出来,接下来,会利用enumerate函数,将args、kwargs的任意参数给展开,并按顺序输出。最后,它再将func以这些参数包装回去,继续进行函数的执行和结果返回。

那么这个decorator如何用呢?

很简单,最上面的那个函数,只要这样改就可以了。

@log_func(log)
def load_modules(self, *args, **kwargs):
    do_something()

这样,如法炮制,就可以加在任何函数上面,完全用复制和粘贴就可以跟踪函数的调用及参数。如果把这个decorator稍微加工一下,就可以也把返回值也输出。

我这个decorator比较简单,用起来也比较简单,但切合decorator的主要思路:

  • 取得函数名以及所有参数
  • 在执行函数前或执行函数后输出相关信息

因此,当我们遇到需要在执行函数前进行的一些检测或预处理(pre),或者后处理(post),同时这部分操作又可以抽象出来的时候,decorator发挥的时候,就到了!

欢迎使用图拉鼎开发的产品

奇点 - 轻轻松松刷微博

为 iOS 设计的第三方微博客户端,简洁高效、标准时间线等特性。App Store 免费下载使用。

10 Comments

asy

Python 的装饰器一直都是一个很好很强大的存在,怎么能说没用呢?Django的View cache,bottle的route都是使用装饰器的好例子啊。

TualatriX 回复 @asy

嗯,Django的decorator是用的更多,我应该把标题改成:如何自己制作decorator并应用 :)

哈,我也写过类似的东西,还带返回值和调用 id 哦: https://github.com/lilydjwg/winterpy/blob/master/pylib/myutils.py#L179

TualatriX 回复 @依云

赞~

非常喜欢主席这种风格的文章..

TualatriX 回复 @scriptkids

多谢喜欢!这样我就有动力写了 :D

Dig

Django 里面才认识装饰器的,感觉是在由装饰器调用目标函数,然后自己还会偷偷的干点私活

嗯嗯,之前有项目的守护进程,我也用装饰器来打日志。不过现在那一套被换成了Gearman了

hbprotoss

好文。我发现decorator对某些既没文档又少注释的代码很有用,每个函数都加个打印函数名和参数的decorator,这样就能大概知道实现一个功能的函数调用路径:D

Leave a Comment