python装饰器理解

python装饰器


理解 python中的装饰器,今天就说说这个问题!

标准装饰器写法

@makebold
def say():
return “Hello”
def makebold(fn):
def wrapped():
retuurn ““ + fn() + “
return wrapped

理解装饰器


我们先来看个小例子,好像所有说装饰器的人都说这个例子,不管了,哈哈哈!

def foo():
print ‘in foo()’
foo()

这是一个相当简单的函数,对的,甚至很无聊,但是我们就从这个来引出装饰器,提个需求,现在小白想要看看这个函数执行需要多长时间,于是可以这样做:

import time
def foo():
start = time.clock()
print ‘in foo()’
end = time.clock()
print ‘used: ‘, end - start

foo()

ok,现在小白的功能实现了,他很开心,但是这时他发现了foo2函数,并同样产生了兴趣,那怎么办,同样的代码在copy一下,NO!还记得吗,函数在python中是一等公民,那么我们可以考虑重新定义一个函数timeit,将foo的引用传递给他,然后在timeit中调用foo并进行计时,这样,我们就达到了不改动foo定义的目的,而且,不论小白看了多少个函数,我们都不用去修改函数定义了!

import time
def foo():
print ‘in foo()’
def timeit(func):
start = time.clock()
func()
end = time.clock()
print ‘used: ‘, end - start
timeit(foo)

看起来逻辑上并没有问题,一切都很美好并且运作正常!……等等,我们似乎修改了调用部分的代码。原本我们是这样调用的:foo(),修改以后变成了:timeit(foo)。这样的话,如果foo在N处都被调用了,你就不得不去修改这N处的代码。或者更极端的,考虑其中某处调用的代码无法修改这个情况,比如:这个函数是你交给别人使用的。

既然如此,我们就来想想办法不修改调用的代码;如果不修改调用代码,也就意味着调用foo()需要产生调用timeit(foo)的效果。我们可以想到将timeit赋值给foo,但是timeit似乎带有一个参数……想办法把参数统一吧!如果timeit(foo)不是直接产生调用效果,而是返回一个与foo参数列表一致的函数的话……就很好办了,将timeit(foo)的返回值赋值给foo,然后,调用foo()的代码完全不用修改!

#-- coding: UTF-8 --
import time
def foo():
print ‘in foo()’
def timeit(func):
def wrapper():
start = time.clock()
func()
end =time.clock()
print ‘used:’, end - start
return wrapper
foo = timeit(foo)
foo()

这样,一个简易的计时器就做好了!我们只需要在定义foo以后调用foo之前,加上foo = timeit(foo),就可以达到计时的目的,这也就是装饰器的概念,看起来像是foo被timeit装饰了。在在这个例子中,函数进入和退出时需要计时,这被称为一个横切面(Aspect),这种编程方式被称为面向切面的编程(Aspect-Oriented Programming)。与传统编程习惯的从上往下执行方式相比较而言,像是在函数执行的流程中横向地插入了一段逻辑。在特定的业务领域里,能减少大量重复代码。面向切面编程还有相当多的术语,这里就不多做介绍,感兴趣的话可以去找找相关的资料。