python语言:装饰器原理
.?裝飾器
????????裝飾器是 Python 的重要組成部分。簡而言之:它們是修改其他功能的功能。它們有助于使我們的代碼更短、更 Pythonic。大多數(shù)初學(xué)者不知道在哪里使用它們,所以我將分享一些裝飾器可以讓你的代碼更簡潔的地方。這可能是最難掌握的概念之一。我們將一步一步地進(jìn)行,以便您完全理解它。
一. 函數(shù)也是對象
????????首先要搞清楚函數(shù)的性質(zhì):
def hi(name="yasoob"):return "hi " + nameprint(hi()) # output: 'hi yasoob'# We can even assign a function to a variable like greet = hi # We are not using parentheses here because we are not calling the function hi # instead we are just putting it into the greet variable. Let's try to run thisprint(greet()) # output: 'hi yasoob'# Let's see what happens if we delete the old hi function! del hi print(hi()) #outputs: NameErrorprint(greet()) #outputs: 'hi yasoob'eer結(jié)論1:函數(shù)可以當(dāng)對象變量用。
?
二、在函數(shù)中定義函數(shù)
????????因此,這些是功能方面的基礎(chǔ)知識。讓我們把你的知識更進(jìn)一步。在 Python 中,我們可以在其他函數(shù)中定義函數(shù):
def hi(name="yasoob"):print("now you are inside the hi() function")def greet():return "now you are in the greet() function"def welcome():return "now you are in the welcome() function"print(greet())print(welcome())print("now you are back in the hi() function")hi() #output:now you are inside the hi() function # now you are in the greet() function # now you are in the welcome() function # now you are back in the hi() function# This shows that whenever you call hi(), greet() and welcome() # are also called. However the greet() and welcome() functions # are not available outside the hi() function e.g:greet() #outputs: NameError: name 'greet' is not defined????????所以現(xiàn)在我們知道我們可以在其他函數(shù)中定義函數(shù)。換句話說:我們可以制作嵌套函數(shù)。現(xiàn)在你需要再學(xué)習(xí)一件事,函數(shù)也可以返回函數(shù)。
三.從函數(shù)內(nèi)部返回函數(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 greetelse:return welcomea = hi() print(a) #outputs: <function greet at 0x7f2143c01500>#This clearly shows that `a` now points to the greet() function in hi() #Now try thisprint(a()) #outputs: now you are in the greet() function????????再看一遍代碼。在 if/else 子句中,我們返回的是 greet 和welcome,而不是 greet() 和welcome()。這是為什么?這是因為當(dāng)你在它后面加上一對括號時,函數(shù)就會被執(zhí)行;而如果你不把括號放在它后面,那么它可以被傳遞并可以分配給其他變量而不執(zhí)行它。你明白了嗎?讓我更詳細(xì)地解釋一下。當(dāng)我們寫 a = hi() 時,hi() 會被執(zhí)行,因為默認(rèn)名稱是 yasoob,所以會返回函數(shù) greet。如果我們將語句更改為 a = hi(name = "ali") 則將返回歡迎函數(shù)。我們還可以執(zhí)行 print hi()() 輸出,現(xiàn)在您在 greet() 函數(shù)中。
四.?將一個函數(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)掌握了了解裝飾器的真正含義所需的所有知識。裝飾器讓您可以在函數(shù)之前和之后執(zhí)行代碼。
五. 寫一個裝飾器
????????在上一個示例中,我們實際上制作了一個裝飾器!讓我們修改之前的裝飾器,制作一個更實用的程序:
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()????????你明白了嗎?我們只是應(yīng)用了之前學(xué)到的原理。這正是裝飾器在 Python 中所做的!它們包裝一個函數(shù)并以一種或另一種方式修改其行為。現(xiàn)在您可能想知道為什么我們沒有在代碼中的任何地方使用@?這只是組成裝飾功能的一種簡短方法。下面是我們?nèi)绾问褂?@ 運行前面的代碼示例。
@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 中的工作方式有一個基本的了解。現(xiàn)在我們的代碼有一個問題。如果我們運行:
print(a_function_requiring_decoration.__name__) # Output: wrapTheFunction????????這不是我們所期望的!它的名字是“a_function_requiring_decoration”。好吧,我們的函數(shù)被 wrapTheFunction 取代了。它覆蓋了我們函數(shù)的名稱和文檔字符串。幸運的是,Python 為我們提供了一個簡單的函數(shù)來解決這個問題,那就是 functools.wraps。讓我們修改之前的示例以使用 functools.wraps:
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_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)在好多了。讓我們繼續(xù)學(xué)習(xí)裝飾器的一些用例。Blueprint:
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 runningcan_run = False print(func()) # Output: Function will not run????????注意:@wraps 接受一個要裝飾的函數(shù),并添加復(fù)制函數(shù)名稱、文檔字符串、參數(shù)列表等的功能。這允許我們在裝飾器中訪問預(yù)裝飾函數(shù)的屬性。
5.1. 應(yīng)用
????????現(xiàn)在讓我們看一下裝飾器真正閃耀的領(lǐng)域,以及它們的使用使事情變得非常容易管理。下面看看裝飾器的管理應(yīng)用。
5.2. 用于權(quán)限管理
????????裝飾器可以幫助檢查某人是否被授權(quán)使用 Web 應(yīng)用程序中的端點。它們廣泛用于 Flask Web 框架和 Django。這是一個使用基于裝飾器的身份驗證的示例:
Example :
from functools import wrapsdef requires_auth(f):@wraps(f)def decorated(*args, **kwargs):auth = request.authorizationif not auth or not check_auth(auth.username, auth.password):authenticate()return f(*args, **kwargs)return decorated5.3. 用于日志管理
????????日志記錄是裝飾者大放異彩的另一個領(lǐng)域。這是一個例子:
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@logit def addition_func(x):"""Do some math."""return x + xresult = addition_func(4) # Output: addition_func was called我相信你已經(jīng)在考慮裝飾器的一些巧妙用途。
六. 裝飾器參數(shù)
????????想一想,@wraps 不也是一個裝飾器嗎?但是,它需要一個參數(shù),就像任何普通函數(shù)一樣。那么,為什么我們也不能這樣做呢?
????????這是因為當(dāng)您使用 @my_decorator 語法時,您正在應(yīng)用一個將單個函數(shù)作為參數(shù)的包裝函數(shù)。請記住,Python 中的一切都是對象,這包括函數(shù)!考慮到這一點,我們可以編寫一個返回包裝函數(shù)的函數(shù)。
6.1.函數(shù)裝飾器
????????讓我們回到我們的日志示例,并創(chuàng)建一個包裝器,讓我們指定要輸出到的日志文件。
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 appendwith open(logfile, 'a') as opened_file:# Now we log to the specified logfileopened_file.write(log_string + '\n')return func(*args, **kwargs)return wrapped_functionreturn 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 string6.2. 類裝飾器
????????現(xiàn)在我們在生產(chǎn)環(huán)境中使用了 logit 裝飾器,但是當(dāng)我們的應(yīng)用程序的某些部分被認(rèn)為是關(guān)鍵時,故障可能需要立即引起注意。假設(shè)有時您只想登錄到文件。其他時候,您希望發(fā)送電子郵件,以便引起您的注意,并且仍然保留日志以供您自己記錄。這是使用繼承的一個例子,但到目前為止我們只看到了用于構(gòu)建裝飾器的函數(shù)。
class logit(object):_logfile = 'out.log'def __init__(self, func):self.func = funcdef __call__(self, *args):log_string = self.func.__name__ + " was called"print(log_string)# Open the logfile and appendwith open(self._logfile, 'a') as opened_file:# Now we log to the specified logfileopened_file.write(log_string + '\n')# Now, send a notificationself.notify()# return base funcreturn self.func(*args)def notify(self):# logit only logs, no morepass????????這個實現(xiàn)還有一個額外的優(yōu)點,就是比嵌套函數(shù)方法更簡潔,并且包裝函數(shù)仍然會使用與以前相同的語法:
logit._logfile = 'out2.log' # if change log file @logit def myfunc1():passmyfunc1() # Output: myfunc1 was called現(xiàn)在,讓我們將 logit 子類化以添加電子郵件功能(盡管此處不會涉及此主題)。
class email_logit(logit):'''A logit implementation for sending emails to adminswhen the function is called.'''def __init__(self, email='admin@myproject.com', *args, **kwargs):self.email = emailsuper(email_logit, self).__init__(*args, **kwargs)def notify(self):# Send an email to self.email# Will not be implemented herepass????????從這里開始,@email_logit 的工作方式與 @logit 類似,但除了記錄日志外,還會向管理員發(fā)送一封電子郵件。
總結(jié)
以上是生活随笔為你收集整理的python语言:装饰器原理的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 公式编辑语言:LaTeX/Advance
- 下一篇: python知识:*args 和**kw