裝飾器(Decorators)是Python的一個重要部分。簡單地說:他們是修改其他函數(shù)的功能的函數(shù)。他們有助于讓我們的代碼更簡短,也更符合Python規(guī)范(Pythonic)。
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 wrapTheFunctiondef 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()
上面的代碼可以更簡潔一些:
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@a_new_decoratordef 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 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(注意語句位置,須位于被裝修函數(shù)之前)等價于下列語句:
a_function_requiring_decoration = a_new_decorator(a_function_requiring_decoration)
如果需要獲取被裝飾函數(shù)的函數(shù)名,需要使用functools.wraps函數(shù):
from functools import wrapsdef 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_decoratordef 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
否則,print(a_function_requiring_decoration.__name__)的返回結果將是wrapTheFunction。
裝飾器能有助于檢查某個人是否被授權去使用一個web應用的端點(endpoint)。它們被大量使用于Flask和Django web框架中。這里是一個例子來使用基于裝飾器的授權:
from functools import wrapsdef 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 wrapsdef logit(func): @wraps(func) def with_logging(*args, **kwargs): print(func.__name__ + ” was called”) return func(*args, **kwargs) return with_logging@logitdef addition_func(x): “””Do some math.””” return x + xresult = addition_func(4)# Output: addition_func was called
裝飾器也可以帶參數(shù),我們將上面日志的例子修改一下,允許指定保存日志的位置:
from functools import wrapsdef logit(logfile=’out.log’): def logging_decorator(func): @wraps(func) def wrapped_function(*args, **kwargs): log_string = func.__name__ + ” was called” print(log_string) # Open the logfile and append with open(logfile, ‘a’) as opened_file: # Now we log to the specified logfile opened_file.write(log_string + ”) return func(*args, **kwargs) return wrapped_function return logging_decorator@logit()def myfunc1(): passmyfunc1()# Output: myfunc1 was called# A file called out.log now exists, with the above string@logit(logfile=’func2.log’)def myfunc2(): passmyfunc2()# Output: myfunc2 was called# A file called func2.log now exists, with the above string
類也可以用來構建裝飾器:
class logit(object): _logfile = ‘out.log’ def __init__(self, func): self.func = func def __call__(self, *args): log_string = self.func.__name__ + ” was called” print(log_string) # Open the logfile and append with open(self._logfile, ‘a’) as opened_file: # Now we log to the specified logfile opened_file.write(log_string + ”) # Now, send a notification self.notify() # return base func return self.func(*args) def notify(self): # logit only logs, no more pass
這個實現(xiàn)有一個附加優(yōu)勢,在于比嵌套函數(shù)的方式更加整潔,而且包裹一個函數(shù)還是使用跟以前一樣的語法:
logit._logfile = ‘out2.log’ # if change log file@logitdef myfunc1(): passmyfunc1()# Output: myfunc1 was called
我們給logit創(chuàng)建子類,來添加email的功能。
class email_logit(logit): ”’ A logit implementation for sending emails to admins when the function is called. ”’ def __init__(self, email=’admin@myproject.com’, *args, **kwargs): self.email = email super(email_logit, self).__init__(*args, **kwargs) def notify(self): # Send an email to self.email # Will not be implemented here pass