Python装饰器的简单理解

161 阅读3分钟

本文记录一下Python装饰器的简单理解

对Python语法尤其是对函数式编程不太熟悉的话可能理解Python的装饰器模式会比较费劲一点,这边简单理解一下Python中的装饰器是个什么东西。

首先,很重要的一点就是Python可以把函数当作对象引用一样传递,所以我们可以把函数当作参数传递给另一个函数,也可以把函数当作另一个函数的返回值,是不是感觉要上天?

加上()的话表示函数在这句代码执行的时候就调用了,不加则表示仅仅当作一个引用传递,并不调用

def func_a(func_r1, func_r2):
    print func_r1()
    return func_r2


def func_b():
    return "hi,kyson.I am func b"


def func_c():
    return "hi,kyson.I am func c"


if __name__ == '__main__':
    print func_a(func_b, func_c)()

一开始看到上面这段的时候其实并没有多少感觉,但是我们只要稍微深究一下就会发现这里面其实可以做很多事情了。

我们可以想一个例子,比如:我现在想要设计一个函数,这个函数可以把其他函数的调用时间打印下来,我们把这个函数暂时命名为:time_cost。

用刚才的想法,我们只需要把任意函数当作参数传到time_cost,然后time_cost调用外部传进来的函数之前和之后计算一下时间,然后打印即可:

def time_cost2(func_r):
    start = time.clock()
    func_r()
    end = time.clock()
    print "cost: %.4f" % (end - start)

这样,我们就可以调用time_cost2函数,并传入真正想要调用的函数,就可以执行函数并打印执行消耗的时间了,cool~

到这里其实装饰器已经完成了,但是还不够。因为每次我们想调用函数的时候只能当作参数传给time_cost2,然后执行time_cost2,这样如果有很多函数需要调用,岂不是每次调用都要换成time_cost2?

这时候Python又露出了蜜汁笑容:不好意思,在我这儿函数名可以赋值给变量

那又怎样?

那就可以把time_cost2函数名称赋值给以真正的函数名称为变量名的变量了!(不好意思,其实很简单的一句话我解释不好)

看看代码吧,就容易理解了:

def time_cost2(func_r):
    start = time.clock()
    func_r()
    end = time.clock()
    print "cost: %.4f" % (end - start)


def func2():
    time.sleep(3)


func2 = time_cost2(func2)

if __name__ == '__main__':
    func2()

我在执行func2()的时候你猜怎么着?报错了!

Traceback (most recent call last):
  File "D:/Dev/Python/Workspace/test/test.py", line 49, in <module>
    func2()
TypeError: 'NoneType' object is not callable

会过来看看代码,原来func2在赋值的时候已经是time_cost2(func2)执行了的,所以func2就是time_cost2(func2)的返回值,而并不是可调用的函数,尴尬。

所以问题就变成了,我怎么样让time_cost2(func2)暂时先不执行,当作可调用的函数赋值给func2?

这时候文章开头提到的一点就很重要了:函数可以当作另一个函数的返回值。

所以这时候代码变成了这样:

def time_cost(func1):
    def time_cost_wrap():
        start = time.clock()
        func1()
        end = time.clock()
        print "cost: %.4f" % (end - start)

    return time_cost_wrap


def func():
    time.sleep(3)


func = time_cost(func)

if __name__ == '__main__':
    func()

time_cost函数里面定义了time_cost_wrap函数,这个函数做的事情就是刚才time_cost函数做的事情,然后time_cost返回了time_cost_wrap函数,这里注意是返回的函数名称,并没有执行time_cost_wrap,所以当我们把time_cost(func)赋值给func变量的时候,实际上我们是把time_cost_wrap函数赋值给了func,这时候我们再执行func()的时候就相当于执行了time_cost_wrap()。

Perfect.

完成代码:

# -*- coding:utf-8 -*-
import time


def func_a(func_r1, func_r2):
    print func_r1()
    return func_r2


def func_b():
    return "hi,kyson.I am func b"


def func_c():
    return "hi,kyson.I am func c"


def time_cost2(func_r):
    start = time.clock()
    func_r()
    end = time.clock()
    print "cost: %.4f" % (end - start)


def func2():
    time.sleep(3)


func2 = time_cost2(func2)


def time_cost(func1):
    def time_cost_wrap():
        start = time.clock()
        func1()
        end = time.clock()
        print "cost: %.4f" % (end - start)

    return time_cost_wrap


def func():
    time.sleep(3)


func = time_cost(func)

if __name__ == '__main__':
    # print func_a(func_b,func_c)
    # func2()
    func()