python之完善decorator

裝飾器可以給函數動態添加功能,但是裝飾器後,再輸出原函數的函數名的話,會發現輸出的不是原函數的函數名了。

def new_fn(text):
    def decorator_new_fn(f):
        def fn(x):
            print ('[%s] call ' + f.__name__ +'()') % (text)
            return f(x)
        return fn
    return decorator_new_fn
@new_fn('important')
def f(x):
    return x*x
print f(3)
print f.__name__, f.__doc__
==> [important'] call f()
==> 9
==> fn None

上面的代碼案例是帶參數的decorator,從上面的代碼案例可以看出:在使用裝飾器後,再訪問原函數的名稱時 不再是 f ,而是對 f 進行加工的新函數 fn,無參數的decorator也是這個部分的函數名。這是因爲代碼 @new_fn('important') 也可以看做是f = new_fn('important')(f),即裝飾器函數把原來的原函數覆蓋住了,所以代碼運行完後原函數 f 指向了裝飾器函數。如果想要在運行完代碼後還能訪問到原函數的屬性,比如訪問原函數的函數名f.__name__等,怎麼才能實現呢?

1、可以在裝飾器裏賦值

      缺點是:只能對暫時使用到的屬性進行操作,函數的屬性很多,對所有的屬性做此種操作不合適。

def new_fn(text):    
    def decorator_new_fn(f):
        def fn(x):
            print ('[%s] call ' + f.__name__ +'()') % (text)
            return f(x)
        fn.__name__ = f.__name__
        fn.__doc__ = f.__doc__
        return fn
    return decorator_new_fn
@new_fn('important')
def f(x):
    return x*x
print f(3)
print f.__name__, f.__doc__
==> [important] call f()
==> 9
==> f None

2、引入functools工具包,自動化引入原函數的所有屬性。

在引入functools後,在代碼裏使用functools下的wraps裝飾器,才能達到引入原函數所有屬性的目的。但要注意wraps裝飾器使用的位置:在傳入原函數 f 後的代碼中。

import functools
def new_fn(text):
    def decorator_new_fn(f):
        @functools.wraps(f)
        def fn(x):
            print ('[%s] call ' + f.__name__ +'()') % (text)
            return f(x)
        return fn
    return decorator_new_fn
@new_fn('important')
def f(x):
    return x*x
print f(3)
print f.__name__, f.__doc__
==> [important] call f()
==> 9
==> f None

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章