從函數(shù)中返回函數(shù)
其實并不需要在一個函數(shù)里去執(zhí)行另一個函數(shù),我們也可以將其作為輸出返回出來:
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`現(xiàn)在指向到hi()函數(shù)中的greet()函數(shù)
#現(xiàn)在試試這個
print(a())
#outputs: now you are in the greet() function
再次看看這個代碼。在if/else語句中我們返回greet和welcome,而不是greet()和welcome()。為什么那樣?這是因為當(dāng)你把一對小括號放在后面,這個函數(shù)就會執(zhí)行;然而如果你不放括號在它后面,那它可以被到處傳遞,并且可以賦值給別的變量而不去執(zhí)行它。
你明白了嗎?讓我再稍微多解釋點細(xì)節(jié)。
當(dāng)我們寫下a = hi(),hi()會被執(zhí)行,而由于name參數(shù)默認(rèn)是yasoob,所以函
數(shù)greet被返回了。如果我們把語句改為a = hi(name = "ali"),那么welcome函數(shù)將被返回。我們還可以打印出hi()(),這會輸出now you are in the greet() function。
將函數(shù)作為參數(shù)傳給另一個函數(shù)
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!
現(xiàn)在你已經(jīng)具備所有必需知識,來進一步學(xué)習(xí)裝飾器真正是什么了。裝飾器讓你在一個函數(shù)的前后去執(zhí)行代碼。
你的第一個裝飾器
在上一個例子里,其實我們已經(jīng)創(chuàng)建了一個裝飾器!現(xiàn)在我們修改下上一個裝飾器,并編寫一個稍微更有用點的程序:
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()
你看明白了嗎?我們剛剛應(yīng)用了之前學(xué)習(xí)到的原理。這正是python中裝飾器做的事情!它們封裝一個函數(shù),并且用這樣或者那樣的方式來修改它的行為。現(xiàn)在你也許疑惑,我們在代碼里并沒有使用@符號?那只是一個簡短的方式來生成一個被裝飾的函數(shù)。這里是我們?nèi)绾问褂?#64;來運行之前的代碼:
@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)
希望你現(xiàn)在對Python裝飾器的工作原理有一個基本的理解。如果我們運行如下代碼會存在一個問題:
print(a_function_requiring_decoration.__name__)
# Output: wrapTheFunction
這并不是我們想要的!Ouput輸出應(yīng)該是“a_function_requiring_decoration”。這里的函數(shù)被warpTheFunction替代了。它重寫了我們函數(shù)的名字和注釋文檔(docstring)。幸運的是Python提供給我們一個簡單的函數(shù)來解決這個問題,那就是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
現(xiàn)在好多了。我們接下來學(xué)習(xí)裝飾器的一些常用場景。
藍(lán)本規(guī)范:
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接受一個函數(shù)來進行裝飾,并加入了復(fù)制函數(shù)名稱、注釋文檔、參數(shù)列表等等的功能。這可以讓我們在裝飾器里面訪問在裝飾之前的函數(shù)的屬性。