您的位置:首页 > 编程语言 > Python开发

Python中的装饰器decorator

2015-11-05 21:31 459 查看
本文是一篇读书笔记,写于一年多前。原文为:http://coolshell.cn/articles/11265.html 本文只是对其中基础和重要部分的选取与简化。

1. 函数Decorator的本质:

@one_decorator
def func():
pass

# 相当于
func = one_deacorator(func)

举例:

def hello(fn):
def wrapper():
print "hello, %s" % fn.__name__
fn()
print "goodbye, %s" % fn.__name__
return wrapper

@hello
def foo():
print "i am foo"


原先的foo函数被hello这个decorator包裹(wrap)了一层了。

故多个Decorator其实是如下的转换:

@decorator_one
@decorator_two
def func():
pass

# 相当于
func = decorator_one(decorator_two(func))


2. 带参数的Decorator:
@decorator(arg1, arg2)
def func():
pass

相当于 func = decorator(arg1,arg2)(func)
注意:这里因为decorator(arg1,arg2)返回的是一个类似函数指针的东西,也就是一个函数,所以func就作为了这个函数的参数了。

3. class式的Decorator

class myDecorator(object):

def __init__(self, fn):
print "inside myDecorator.__init__()"
self.fn = fn

def __call__(self):
self.fn()
print "inside myDecorator.__call__()"

@myDecorator
def aFunction():
print "inside aFunction()"

print "Finished decorating aFunction()"

aFunction()


输出:

inside myDecorator.__init__()
Finished decorating aFunction()
inside aFunction()
inside myDecorator.__call__()


4. Decorator的副作用

被decorator的函数其实已经是另外一个函数了。如果查询一下被decorator之后的原函数名(如foo.__name__)的话,你会发现其输出的是“wrapper”(decorator的函数名),而不是我们期望的“foo”(原函数名),这可能会给将来的程序埋一些坑。

所以,Python的functools包中提供了一个叫wraps的decorator来消除这样的副作用。

wraps的作用就是把原函数的函数名等属性传递给decorator函数。

from functools import wraps
def hello(fn):
@wraps(fn)
def wrapper():
print "hello, %s" % fn.__name__
fn()
print "goodbye, %s" % fn.__name__
return wrapper

@hello
def foo():
'''foo help doc'''
print "i am foo"
pass

foo()
print foo.__name__ #输出 foo
print foo.__doc__  #输出 foo help doc


5. 经典的例子

给函数调用做缓存:

from functools import wraps
def memo(fn):
cache = {}
miss = object()

@wraps(fn)
def wrapper(*args):
result = cache.get(args, miss)
if result is miss:
result = fn(*args)
cache[args] = result
else:
print "Hit Cache! - %s" % args
return result

return wrapper

@memo
def fib(n):
if n < 2:
return n
return fib(n - 1) + fib(n - 2)


执行结果如下:
>>> execfile("1.py")
>>> fib(10)
Hit Cache! - 1
Hit Cache! - 2
Hit Cache! - 3
Hit Cache! - 4
Hit Cache! - 5
Hit Cache! - 6
Hit Cache! - 7
Hit Cache! - 8
55
>>> fib(10)
Hit Cache! - 10
55
>>> fib(8)
Hit Cache! - 8
21
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: