在 python 中,咱們常常能夠看到這樣的函數定義:python
@staticmethod def func(): ... ...
不去看第一句,剩下的就是一個普通的函數定義了,那麼加上第一句app
@staticmethod
是什麼意思?又有什麼用處呢?函數
咱們從字面上能夠猜想,這裏定義的 func 函數是靜態函數,至因而什麼是靜態函數,在本文中就不作介紹了,有意思能夠閱讀這篇文章瞭解。那加上這個以後有什麼用,python 對咱們定義的 func 函數作了什麼操做呢?spa
若是你看過一些關於 staticmethod 的資料的話,你也許會知道最開始定義 staticmethod 的方式是:調試
func = staticmethod(func)
也就是說,加入我要在類 Foo 中定義一個靜態函數,那麼我須要這麼寫:code
class Foo(object): def func(): ... ... func = staticmethod(func) Foo.func()
看一下這種方式,咱們發現明顯很累贅,因此裝飾器就來解救咱們於繁瑣之中,咱們只須要加一個裝飾器,就將函數定義簡化成了:orm
class Foo(object): @staticmethod def func(): ... ... Foo.func()
因此,從這裏咱們就知道了裝飾器就是簡化函數調用的一種方式。get
@wrap def func(): ... ... func()
等價於:it
wrap(func)()
說了一些概念以後,咱們是時候上個例子了,這個例子我想實現的功能就是可以計算函數執行的時間,例如xx 函數執行花費了 3S 之類的,代碼以下:io
def my_decorator(func): def wrapper_fun(): begin_time = time() func() end_time = time() print int(end_time - begin_time) return wrapper_fun @my_decorator def test(): print "test func run" sleep(3) test()
這個例子很簡單,就是計算一下 test 函數執行了多久,咱們解析開來看其實調用就是這樣的:
my_decorator(test)()
而後就會打印出花費的時間。
第一個例子太簡單了,以致於咱們都沒感受到有趣,因此此次咱們加上個參數試試:
def my_decorator(func): def wrapper_fun(): begin_time = time() func() end_time = time() print int(end_time - begin_time) return wrapper_fun @my_decorator def test(arg): print "test func run with arg: {}".format(arg) test('hello')
而後跑一遍,發現果斷出錯了。咱們分析一下問題,這裏的調用解開來其實就是:
my_decorator(test)('hello')
那
my_decorator(test)
返回的是 wrapper_fun(),沒有帶參數,因此代碼執行失敗很正常。既然知道了緣由,那麼修改起來也方便了,就是給 wrapper_fun 加上參數:
def my_decorator(func): def wrapper_fun(*args, **kwargs): print "decorator begin" func(*args, **kwargs) print "decorator end" return wrapper_fun @my_decorator def test(arg): print "test func run with arg: {}".format(arg) test('hello')
而後跑一遍代碼,運行起來了,而且打印出:
decorator begin test func run with arg: hello decorator end
終於有點意思了,如今咱們能夠調用帶參數的函數了,可是,喜歡瞎折騰的我又有了新的想法,我好想見過裝飾器帶參數的,那這樣怎麼實現呢?根據套路,咱們仍是以解開的函數調用來進行思考:
若是裝飾器帶參數,那麼,咱們是否是能夠這樣子:
my_decorator(test, 'decorator_arg')('hello')
這樣作的話,那麼咱們的裝飾器要怎麼寫呢?這樣子?:
def my_decorator(func, *oargs, **okwargs): def wrapper_fun(*iargs, **ikwargs): print "decorator begin" func(*iargs, **ikwargs) print "decorator end" return wrapper_fun
那明顯不行啊,由於裝飾器是 Python 自帶的語法糖,咱們不能控制它的參數個數,就只能是一個參數,並且就是咱們被封裝的函數,否則就不是裝飾器了。那說明咱們這個方法不行,那就換個思路:
my_decorator('decorator_arg')(test)('hello')
這樣子行不行,這樣子的話,咱們既給裝飾器帶了參數,又給函數帶了參數,那麼轉換成裝飾器能不能實現呢?試一下:
def my_decorator(name): def decorator_func(func): def wrapper_fun(*args, **kwargs): func(*args, **kwargs) return wrapper_fun return decorator_func
看下這個能不能實現咱們的想法,首先
是的,這樣一解析以後就發現是實現了咱們的想法。
目前爲止咱們就已經實現了帶參數函數的裝飾器,裝飾器帶參數的功能,接下來還有什麼問題呢?好像暫時想不到,那咱們就寫個例子看看:
def func(): """function for test""" print "functiong calling" print func.__name__ print func.__doc__
而後執行一遍看下結果:
func function for test
沒什麼問題,接着來看下這個:
def my_decorator(): def decorator_func(func): def wrapper_fun(*args, **kwargs): func(*args, **kwargs) return wrapper_fun return decorator_func @my_decorator def func(): """function for test""" print "functiong calling" print func.__name__ print func.__doc__
而後運行一遍,看看輸出:
decorator_func None
WTF,怎麼了,什麼狀況,個人名字呢?很明顯,如今的 func 已經被隱藏了,取而代之的是 decorator_func,那麼,加入咱們調試代碼或者出 bug 了要怎麼定位到真正的出問題的 func 呢?那仍是須要把 name 和 doc 這些成員變量找回來的。這裏給個坑本身,先不詳解:
from functools import wraps def my_decorator(): def decorator_func(func): @wraps(func) def wrapper_fun(*args, **kwargs): func(*args, **kwargs) return wrapper_fun return decorator_func @my_decorator() def func(): """function for test""" print "functiong calling" print func.__name__ print func.__doc__