1. 程式人生 > >python解答之裝飾器

python解答之裝飾器

介紹 don fun () 內部 student turn cos must

裝飾器,簡而言之就是在不改變原函數任何代碼(包括調用方式)的情況下為函數增加額外的功能。特殊之處在於裝飾器的返回值也是一個函數。 一、裝飾器介紹
下面先舉個簡單的栗子:
import time

def count_time(foo_func): def inner(): print("inner func start!") start_time = time.time() time.sleep(1) # 1s後執行 foo_func() end_time = time.time()
print("the func cost:{:.2f}".format(end_time-start_time)) return inner

@count_time def foo(): # 利用裝飾器計算foo函數執行時間 print("foo execute done!!")

if __name__ == ‘__main__‘: foo() # 調用foo函數
既然說增加了裝飾器,那麽原函數的代碼和調用方式都沒改變。 其中 @count_time 就等同於 foo = count_time(foo)

如果要給裝飾器傳參數。。如下: import time
def outer(name): def count_time(foo_func): def inner(): print("inner func start!") print("裝飾器傳進來的參數是:{}".format(name)) start_time = time.time() time.sleep(1) # 1s後執行 foo_func()
end_time = time.time() print("the func cost:{:.2f}".format(end_time-start_time)) return inner return count_time

@outer(name="jack") def foo(): # 利用裝飾器計算foo函數執行時間 print("foo execute done!!")

if __name__ == ‘__main__‘: foo() # 調用foo函數
若要給裝飾器傳參數,只需要再給函數增加一層函數就行了。
但是由於使用裝飾器後會改變函數的一些內置變量比如__name__,__doc__。看栗子:
import time from functools import wraps

def count_time(foo_func): # @wraps(foo_func) def inner(): """ now in inner func """ print("inner func start!") start_time = time.time() time.sleep(1) # 1s後執行 foo_func() end_time = time.time() print("foo func cost:{:.2f}".format(end_time-start_time)) return inner

@count_time def foo(): """ now in foo func """ # 利用裝飾器計算foo函數執行時間 print("foo func doc is {}".format(foo.__doc__)) # 輸出 now in inner func print("foo func name is {}".format(foo.__name__)) # 輸出 inner print("foo execute done!!")

if __name__ == ‘__main__‘: foo() # 調用foo函數 所以需要糾正回來,該如何呢?來
只需要引入functools模塊下的wraps函數 在inner函數上面使用@wraps(func),即再次裝飾下inner函數即可 然後輸出語句會變為: print("foo func doc is {}".format(foo.__doc__)) # 輸出 now in foo func print("foo func name is {}".format(foo.__name__)) # 輸出 foo

要給被裝飾的函數傳參數: 栗子如下: import time from functools import wraps

def count_time(foo_func): @wraps(func) def inner(*args, **kwargs): """ now in inner func """ print("inner func start!") start_time = time.time() time.sleep(1) # 1s後執行 foo_func(*args, **kwargs) end_time = time.time() print("foo func cost:{:.2f}".format(end_time-start_time)) return inner

@count_time def foo(name, age, *args, **kwargs): """ now in foo func """ # 利用裝飾器計算foo函數執行時間 print(name,age) print(args,kwargs) print("foo execute done!!")

if __name__ == ‘__main__‘: foo("jack", 21, "student", department="信工學院") # 調用foo函數
增加的地方有調用時傳了四個參數,分別是位置參數和關鍵字參數(最後一個是關鍵字參數,關鍵字參數必須在位置參數之後傳入) 在裝飾器內部的inner函數的參數那裏增加了*args, **kwargs,和inner內部的func(*args, **kwargs)這裏 args表示所有的位置參數都在args這個元組裏面 kwargs表示所有的關鍵字參數都在args這個字典裏面

二、基於類實現的裝飾器 看栗子 class Logging(object): def __init__(self, func): self.func = func
def __call__(self, *args, **kwargs): print("[DEBUG]: enter function {func}()".format( func=self.func.__name__)) return self.func(*args, **kwargs)

@Logging def say(something): print( "say {}!".format(something))

say("hello")
輸出: [DEBUG]: enter function say() say hello! 解答:如果用類來實現裝飾器的話,必須實現類中的內置方法__init__和__call__這兩個方法 其中__init__用來接收被裝飾函數的名字,__call__用來對函數進行裝飾以及增加額外的功能
若要給裝飾器傳參數。還是看栗子: class logging(object): def __init__(self, level=‘INFO‘): self.level = level
def __call__(self, func): # 接受函數 def wrapper(*args, **kwargs): print("[{level}]: enter function {func}()".format( level=self.level, func=func.__name__)) func(*args, **kwargs)
return wrapper # 返回函數

@logging(level=‘INFO‘) def say(something): print("say {}!".format(something))
say("hello")
這裏的調用就是給裝飾器傳參數。 __init__方法參數接收的不再是被裝飾函數的函數名,而是裝飾器傳來的參數 __call__方法參數接收被裝飾函數的函數名,裏面再嵌套一層函數用來接收裝飾函數傳的參數
最後,拓展一下wrapt這個裝飾器 import wrapt
@wrapt.decorator def logging(wrapped, instance, args, kwargs): # instance is must need print("[DEBUG]: enter {}()".format(wrapped.__name__)) return wrapped(*args, **kwargs)
@logging def say(something): pass
say("hello")
這樣看起來裝飾器就更加明了了,,不用在裝飾器內部再嵌套函數了。 上面的寫法必須要按照上面的來 其中wrapped就表示被裝飾的函數, 若要給裝飾器傳參數,也是再嵌套一層就可以了。





python解答之裝飾器