按照python官方文檔,函數(shù)定義可以被一個(gè)或者多個(gè)decorator包起來(lái),解釋如下:
@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. 無(wú)參數(shù)裝飾器
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"
解釋
- 編譯器做如下轉(zhuǎn)換:
@hello
def foo # ===> hello(foo)
- foo = hello(foo) = wrapper
- foo此時(shí)僅僅是一個(gè)函數(shù),沒(méi)有被調(diào)用,foo()才會(huì)調(diào)用
這種裝飾器,僅僅將在已有函數(shù)的基礎(chǔ)之上,附加額外的裝飾功能。
到底什么時(shí)候調(diào)用,由使用者決定
Example 2. 帶參數(shù)裝飾器
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"
解釋
- 編譯器做如下轉(zhuǎn)換:
@hello("")
def foo # ===> hello("")(foo)
- foo = hello("")(foo)
- hello("") = wrapper
- foo = wrapper(foo)
- 此時(shí)編譯器已經(jīng)執(zhí)行wrapper函數(shù)
- 由于wrapper沒(méi)有返回值,foo=None
這種裝飾器就不一樣了,函數(shù)定義期間就被賦予執(zhí)行的行為
設(shè)想:這可以被用來(lái)做某些初始化動(dòng)作
從上面的例子分析可以看到,被decorator的函數(shù)其實(shí)已經(jīng)是另外一個(gè)函數(shù)了,對(duì)于最前面那個(gè)hello.py的例子來(lái)說(shuō),如果你查詢(xún)一下foo.__name__的話,你會(huì)發(fā)現(xiàn)其輸出的是“wrapper”,而不是我們期望的“foo”,這會(huì)給我們的程序埋一些坑。所以,Python的functool包中提供了一個(gè)叫wrap的decorator來(lái)消除這樣的副作用
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
應(yīng)用1. 給函數(shù)調(diào)用做緩存
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)
應(yīng)用2. 注冊(cè)回調(diào)函數(shù)
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')
參考自:coolshell