單獨一章複習:Python 函數裝飾器,廖雪峯講裝飾器html
子函數寫在外面貌似也能夠,可這樣就少了「封裝性」。python
def greet(): return "now you are in the greet() function" def welcome(): return "now you are in the welcome() function" def hi(name="yasoob"): if name == "yasoob": return greet else: return welcome
a = hi()
print(a) #outputs: <function greet at 0x7f2143c01500> print(a()) #outputs: now you are in the greet() function
def hi(): return "hi yasoob!" def doSomethingBeforeHi(func): print("I am doing some boring work before executing hi()") print(func()) doSomethingBeforeHi(hi) #outputs:I am doing some boring work before executing hi() # hi yasoob!
a_function_requiring_decoration經過a_new_decorator得到了加強版的本身。git
def a_new_decorator(a_func): def wrapTheFunction(): print("I am doing some boring work before executing a_func()") a_func() print("I am doing some boring work after executing a_func()") return wrapTheFunction def a_function_requiring_decoration(): print("I am the function which needs some decoration to remove my foul smell")
a_function_requiring_decoration() #outputs: "I am the function which needs some decoration to remove my foul smell" a_function_requiring_decoration = a_new_decorator(a_function_requiring_decoration) # <---- 函數(A)把本身"裝飾"了一下,加強了功能 #now a_function_requiring_decoration is wrapped by wrapTheFunction() a_function_requiring_decoration() #outputs:I am doing some boring work before executing a_func() # I am the function which needs some decoration to remove my foul smell # I am doing some boring work after executing a_func()
寫好a_new_decorator裝飾器函數後,只要在須要「被裝飾」的函數上添加@語法糖就行了。web
如此,就省掉了上述中的 函數(A)的步驟。閉包
一篇通俗易懂的博文:這是我見過最全面的Python裝飾器詳解!沒有學不會這種說法!app
@a_new_decorator def a_function_requiring_decoration(): """Hey you! Decorate me!""" print("I am the function which needs some decoration to " "remove my foul smell")
However,只是表面上完美,抽象的還不夠完全。 框架
上面的例子沒有參數,若是有參數會如何?函數
並且只會寫「一個裝飾器」,因此「參數個數不定」也須要考慮進去。ui
# 萬能裝飾器,修飾各類參數
def celebrator(func): def inner(*args, **kwargs): print('我是新功能') func(*args, **kwargs) return inner
@celebrator def myprint(a): print(a) myprint('python小白聯盟')
def celebrator(func): def inner(*args, **kwargs): print('我是新功能') ret = func(*args, **kwargs)
return ret return inner
裝飾的函數,若是有返回值,則一致;若是沒有返回值,則返回 None。spa
最外層的語法糖先執行。
使用帶參的語法糖來進行簡化;也就是再嵌套一層函數,同時也保證了「參數的獨立性」
有點相似於「閉包」的如下這個例子,保持了子函數的參數的獨立性。
def count():
------------------------- def f(j): # 由於通過了函數的封裝,因此保持了函數內部變量的獨立性 def g(): return j*j return g ------------------------- fs = [] for i in range(1, 4): fs.append(f(i)) # f(i)馬上被執行,所以i的當前值被傳入f() return fs f1, f2, f3 = count()
以下可見,返回的函數名字是裝飾器中的wrap函數的名字,這是個封裝缺陷。
print(a_function_requiring_decoration.__name__) # Output: wrapTheFunction
經過wraps告訴wrap函數:「你的扮演人物的身份是誰」。
注意:@wraps接受一個函數來進行裝飾,並加入了複製函數名稱、註釋文檔、參數列表等等的功能。這可讓咱們在裝飾器裏面訪問在裝飾以前的函數的屬性。
from functools import wraps def a_new_decorator(a_func): @wraps(a_func)
def wrapTheFunction(): print("I am doing some boring work before executing a_func()") a_func() print("I am doing some boring work after executing a_func()") return wrapTheFunction @a_new_decorator def a_function_requiring_decoration(): """Hey yo! Decorate me!""" print("I am the function which needs some decoration to " "remove my foul smell") print(a_function_requiring_decoration.__name__) # Output: a_function_requiring_decoration
裝飾器能有助於檢查某我的是否被受權去使用一個web應用的端點(endpoint)。它們被大量使用於Flask和Django web框架中。
這裏是一個例子來使用基於裝飾器的受權:
from functools import wraps def requires_auth(f): @wraps(f) def decorated(*args, **kwargs): auth = request.authorization if not auth or not check_auth(auth.username, auth.password): authenticate() return f(*args, **kwargs) return decorated
日誌是裝飾器運用的另外一個亮點。這是個例子:
from functools import wraps def logit(func): @wraps(func) def with_logging(*args, **kwargs): print(func.__name__ + " was called") return func(*args, **kwargs) return with_logging @logit def addition_func(x): """Do some math.""" return x + x result = addition_func(4) # Output: addition_func was called
【在類的章節再詳解】
咱們的應用的某些部分還比較脆弱時,異常也許是須要更緊急關注的事情。比方說有時你只想打日誌到一個文件。而有時你想把引發你注意的問題發送到一個email,同時也保留日誌,留個記錄。這是一個使用繼承的場景,但目前爲止咱們只看到過用來構建裝飾器的函數。
幸運的是,類也能夠用來構建裝飾器。那咱們如今以一個類而不是一個函數的方式,來從新構建logit。
from functools import wraps class logit(object): def __init__(self, logfile='out.log'): self.logfile = logfile def __call__(self, func): @wraps(func) def wrapped_function(*args, **kwargs): log_string = func.__name__ + " was called" print(log_string) # 打開logfile並寫入 with open(self.logfile, 'a') as opened_file: # 如今將日誌打到指定的文件 opened_file.write(log_string + '\n') # 如今,發送一個通知 self.notify() return func(*args, **kwargs) return wrapped_function def notify(self): # logit只打日誌,不作別的 pass
如今,咱們給 logit 建立子類,來添加 email 的功能。
class email_logit(logit): ''' 一個logit的實現版本,能夠在函數調用時發送email給管理員 ''' def __init__(self, email='admin@myproject.com', *args, **kwargs): self.email = email super(email_logit, self).__init__(*args, **kwargs) def notify(self): # 發送一封email到self.email # 這裏就不作實現了 pass
End.