[转]理解Python装饰器

作者: NickYang 分类: 程序开发 发布时间: 2016-05-21 14:56
作者:xlzd
链接:http://www.zhihu.com/question/26930016/answer/81263287
来源:知乎
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。简单来讲,可以不严谨地把Python的装饰器看做一个包装函数的函数。

比如,有一个函数:

def func():
    print 'func() run.'

if '__main__' == __name__:
    func()

运行后将输出:

func() run.

现在需要在函数运行前后打印一条日志, 但是又不希望或者没有权限修改函数内部的结构, 就可以用到装饰器(decorator):

def log(function):
    def wrapper(*args, **kwargs):
        print 'before function [%s()] run.' % function.__name__
        rst = function(*args, **kwargs)
        print 'after function [%s()] run.' % function.__name__
        return rst
    return wrapper 

@log
def func():
    print 'func() run.'

if '__main__' == __name__:
    func()

对于原来的函数”func()”并没有做修改,而是给其使用了装饰器log,运行后的输出为:

before function [func()] run.
func() run.
after function [func()] run.

把”@log”放到func()函数定义的地方,相当于执行了如下语句:

func = log(func)

因为log()返回了一个函数, 所以原本的func指向了log()返回的函数wrapper。wrapper的参数列表为(*args, **kwargs), 所以其可以接受所有的参数调用, 在wrapper中,先打印了一行

‘before function [%s()] run.’ % function.__name__

(在Python中函数也是对象,函数的__name__是它的名字),然后执行了原来的函数并记录了返回值,在输出

‘after function [%s()] run.’ % function.__name__

后返回了函数的执行结果。

如果decorator本身需要传入参数,那就需要编写一个返回decorator的decorator。比如在Flask中:

@app.route('/')
def index():
    return 'hello, world!'

实现如下:

import functools

def log(text=''):
    def decorator(function):
        @functools.wraps(function)
        def wrapper(*args, **kwargs):
            print 'before function [%s()] run, text: [%s].' % (function.__name__, text)
            rst = function(*args, **kwargs)
            print 'after function [%s()] run, text: [%s].' % (function.__name__, text)
            return rst 
        return wrapper
    return decorator

@log('log text')
def func():
    print 'func() run.'

if '__main__' == __name__:
    func()

输出如下:

before function [func()] run, text: [log text].
func() run.
after function [func()] run, text: [log text].

最后脑洞小开一下, 有没有办法实现既支持不带参数(如log), 又支持带参数(如log(‘text’))的decorator吗?

import functools

def log(argument):
    if not callable(argument):
        def decorator(function):
            @functools.wraps(function)
            def wrapper(*args, **kwargs):
                print 'before function [%s()] run, text: [%s].' % (function.__name__, text)
                rst = function(*args, **kwargs)
                print 'after function [%s()] run, text: [%s].' % (function.__name__, text)
                return rst 
            return wrapper
        return decorator
    def wrapper(*args, **kwargs):
        print 'before function [%s()] run.' % function.__name__
        rst = function(*args, **kwargs)
        print 'after function [%s()] run.' % function.__name__
        return rst
    return wrapper

如上~~~

如果觉得我的文章对您有用,请随意打赏。您的支持将鼓励我继续创作!

发表评论

电子邮件地址不会被公开。 必填项已用*标注