按照python官方文档,函数定义可以被一个或者多个decorator包起来,解释如下:
@f1(arg)
@f2
def func(): pass
等同于
def func(): pass
func = f1(arg)(f2(func))
而且:Decorator expressions are evaluated when the function is defined, in the scope that contains the function definition
Example 1. 无参数装饰器
def hello(fn):
def wrapper():
print "hello, %s" % fn.__name__
fn()
print "goodby, %s" % fn.__name__
return wrapper
@hello
def foo():
print "i am foo"
解释
- 编译器做如下转换:
@hello
def foo # ===> hello(foo)
- foo = hello(foo) = wrapper
- foo此时仅仅是一个函数,没有被调用,foo()才会调用
这种装饰器,仅仅将在已有函数的基础之上,附加额外的装饰功能。
到底什么时候调用,由使用者决定
Example 2. 带参数装饰器
def hello(dummy=""):
def wrapper(fn):
print "hello, %s" % fn.__name__
fn()
print "goodby, %s" % fn.__name__
return wrapper
@hello("")
def foo():
print "i am foo"
解释
- 编译器做如下转换:
@hello("")
def foo # ===> hello("")(foo)
- foo = hello("")(foo)
- hello("") = wrapper
- foo = wrapper(foo)
- 此时编译器已经执行wrapper函数
- 由于wrapper没有返回值,foo=None
这种装饰器就不一样了,函数定义期间就被赋予执行的行为
设想:这可以被用来做某些初始化动作
从上面的例子分析可以看到,被decorator的函数其实已经是另外一个函数了,对于最前面那个hello.py的例子来说,如果你查询一下foo.__name__的话,你会发现其输出的是“wrapper”,而不是我们期望的“foo”,这会给我们的程序埋一些坑。所以,Python的functool包中提供了一个叫wrap的decorator来消除这样的副作用
from functools import wraps
def hello(fn):
@wraps(fn)
def wrapper():
print "hello, %s" % fn.__name__
fn()
print "goodby, %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
应用1. 给函数调用做缓存
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
return result
return wrapper
@memo
def fib(n):
if n < 2:
return n
return fib(n - 1) + fib(n - 2)
应用2. 注册回调函数
class MyApp():
def __init__(self):
self.func_map = {}
def register(self, name):
def func_wrapper(func):
self.func_map[name] = func
return func
return func_wrapper
def call_method(self, name=None):
func = self.func_map.get(name, None)
if func is None:
raise Exception("No function registered against - " + str(name))
return func()
app = MyApp()
@app.register('/')
def main_page_func():
return "This is the main page."
@app.register('/next_page')
def next_page_func():
return "This is the next page."
print app.call_method('/')
print app.call_method('/next_page')