目錄
從函數中返回函數
將函數作為參數傳給另一個函數
你的第一個裝飾器
其實並不需要在一個函數裡去執行另一個函數,我們也可以將其作為輸出返回出來:
def hi(name="yasoob"):
def greet():
return "now you are in the greet() function"
def welcome():
return "now you are in the welcome() function"
if name == "yasoob":
return greet
else:
return welcome
a = hi()
print(a)
#outputs: <function greet at 0x7f2143c01500>
#上面清晰地展示了`a`現在指向到hi()函數中的greet()函數
#現在試試這個
print(a())
#outputs: now you are in the greet() function
再次看看這個代碼。在if/else語句中我們返回greet和welcome,而不是greet()和welcome()。為什麼那樣?這是因為當你把一對小括號放在後面,這個函數就會執行;然而如果你不放括號在它後面,那它可以被到處傳遞,並且可以賦值給別的變量而不去執行它。
你明白了嗎?讓我再稍微多解釋點細節。
當我們寫下a = hi(),hi()會被執行,而由於name參數默認是yasoob,所以函
數greet被返回了。如果我們把語句改為a = hi(name = "ali"),那麼welcome函數將被返回。我們還可以打印出hi()(),這會輸出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!
現在你已經具備所有必需知識,來進一步學習裝飾器真正是什麼了。裝飾器讓你在一個函數的前後去執行代碼。
在上一個例子裡,其實我們已經創建了一個裝飾器!現在我們修改下上一個裝飾器,並編寫一個稍微更有用點的程序:
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)
#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()
你看明白了嗎?我們剛剛應用了之前學習到的原理。這正是python中裝飾器做的事情!它們封裝一個函數,並且用這樣或者那樣的方式來修改它的行為。現在你也許疑惑,我們在代碼裡並沒有使用@符號?那只是一個簡短的方式來生成一個被裝飾的函數。這裡是我們如何使用@來運行之前的代碼:
@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")
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()
#the @a_new_decorator is just a short way of saying:
a_function_requiring_decoration=a_new_decorator(a_function_requiring_decoration)
希望你現在對Python裝飾器的工作原理有一個基本的理解。如果我們運行如下代碼會存在一個問題:
print(a_function_requiring_decoration.__name__)
# Output: wrapTheFunction
這並不是我們想要的!Ouput輸出應該是“a_function_requiring_decoration”。這裡的函數被warpTheFunction替代了。它重寫了我們函數的名字和注釋文檔(docstring)。幸運的是Python提供給我們一個簡單的函數來解決這個問題,那就是functools.wraps。我們修改上一個例子來使用
functools.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
現在好多了。我們接下來學習裝飾器的一些常用場景。
藍本規范:
from functools import wraps
def decorator_name(f):
@wraps(f)
def decorated(*args, **kwargs):
if not can_run:
return "Function will not run"
return f(*args, **kwargs)
return decorated
@decorator_name
def func():
return("Function is running")
can_run = True
print(func())
# Output: Function is running
can_run = False
print(func())
# Output: Function will not run
注意:@wraps接受一個函數來進行裝飾,並加入了復制函數名稱、注釋文檔、參數列表等等的功能。這可以讓我們在裝飾器裡面訪問在裝飾之前的函數的屬性。