裝飾器可以給函數動態添加功能,但是裝飾器後,再輸出原函數的函數名的話,會發現輸出的不是原函數的函數名了。
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