一篇文章帶你了解Python中的裝飾器
前言
本文將帶你學(xué)習(xí)裝飾器在 Python 中的工作原理,如果在函數(shù)和類中使用裝飾器,如何利用裝飾器避免代碼重復(fù)(DRY 原則,Don’t Repeat Yourself )。
Python 中的裝飾器是什么
裝飾器在 Python中是一個非常強大和有用的工具,因為它允許程序員修改函數(shù)或類的行為。裝飾器允許我們包裝另一個函數(shù),以擴展包裝函數(shù)的行為,而無需修改基礎(chǔ)函數(shù)定義。這也被稱為元編程,因為程序本身在程序運行時會嘗試修改自身的另一部分。
裝飾器是?語法糖: ?在代碼中利用更簡潔流暢的語法實現(xiàn)更為復(fù)雜的功能。
我們知道,Python 一切皆對象。這意味著 Python 中的函數(shù)可以用作參數(shù)或作為參數(shù)傳遞。一等函數(shù)的屬性:
- 函數(shù)是 Object 類型的實例。
- 可以將函數(shù)存儲在變量中。
- 可以將該函數(shù)作為參數(shù)傳遞給另一個函數(shù)。
- 可以從函數(shù)中返回函數(shù)。
- 可以將它們存儲在數(shù)據(jù)結(jié)構(gòu)中,例如哈希表,列表等。
讓我們看一個這樣的例子。
def hello(): print('Welcome to Python Decorator!') another_hello = hello() another_hello # Welcome to Python Decorator!
定義了一個 ??hello()?? 函數(shù),然后將 hello 函數(shù)分配給 another_hello 變量,然后調(diào)用這個變量,得到的結(jié)果是 hello 函數(shù)被執(zhí)行。
既然 Python 中的函數(shù)是對象,那么除了可以簡單的調(diào)用之外,就可以把函數(shù)作為對象傳遞給另一個函數(shù)。
def print_welcome(): print('Welcome to Python Decorator!') def print_hello(func): def inner(): print('Hello!') func() return inner decorated = print_hello(print_welcome) decorated() # Hello! # Welcome to Python Decorator!
語法糖
但是,上面的代碼使用了內(nèi)部函數(shù)我們可以通過簡單地用裝飾器函數(shù) ??print_hello()??? 來裝飾 ??print_welcome()?? 函數(shù)。
裝飾器可以簡化我們的操作。功能完全一樣,但它的代碼更簡潔。即通過 ??@?? 符號簡化裝飾器的使用,如下所示:
def print_hello(func): def inner(): print('Hello!') func() return inner @print_hello def print_welcome(): print('Welcome to Python Decorator!') print_welcome() # Hello! # Welcome to Python Decorator!
通過這樣做,我們能夠消除將一個函數(shù)顯式傳遞到另一個函數(shù)中的使用。Python 裝飾器隱式處理這一點。
使用 Python 裝飾器修改函數(shù)行為
使用 Python 裝飾器對函數(shù)進行計時
為了演示它們的實用性,讓我們構(gòu)建一個函數(shù),該函數(shù)采用另一個函數(shù)并對其執(zhí)行進行計時。在這里,使用裝飾器的好處是它允許我們遵循 DRY 編程原則。
裝飾器可用于測量函數(shù)執(zhí)行所需的時間。 如果你定義一個簡單的睡眠函數(shù),以計算該函數(shù)的運行時。
import time def timeit(func): def timed(): start = time.time() result = func() end = time.time() print(f'Program took {(end - start) * 1000}s to run') return result return timed @timeit def print_welcome(): print('Welcome to Python Decorator!') print_welcome() # Welcome to Python Decorator! # Program took 0.0s to run
分析一下上面的代碼:
- 定義了一個函數(shù) ?
?timeit()?
? 接受另一個函數(shù) - 該函數(shù)還有另一個內(nèi)部函數(shù) ?
?timed()?
? - 函數(shù)跟蹤開始時間,執(zhí)行修飾函數(shù),跟蹤結(jié)束時間,計算差值并返回結(jié)果
- 最后,外層函數(shù)返回內(nèi)層函數(shù)
當(dāng)我們將此裝飾器函數(shù)應(yīng)用于我們的函數(shù) ??print_welcome()?
? 時,首先會返回歡迎問候語,然后顯示執(zhí)行時間。
使用 Python 裝飾器將有用信息記錄到終端
與上面的例子類似,我們可以在程序運行時使用裝飾器將有用的信息打印到終端。例如,我們可能想知道正在運行哪個函數(shù)以及當(dāng)前時間。也可以使用裝飾器傳遞到日志文件:
from datetime import datetime def log_info(func): def inner(): print(f'Starting run at {datetime.now()}') print(f'Running {func.__name__}') func() return inner @log_info def print_welcome(): print('Welcome to Python Decorator!') print_welcome() # Starting run at 2022-03-27 23:26:38.473310 # Running print_welcome # Welcome to Python Decorator!
在上面的示例中,在運行函數(shù)之前,我們的裝飾器打印當(dāng)前日期和時間以及將要運行的函數(shù)的名稱。如果您正在運行較長的腳本,并且只是想知道程序的位置,這可能很有用。
Web app 中使用的裝飾器
讓我們以 Web 應(yīng)用程序的用例為例。當(dāng)您在 Flask 中構(gòu)建 Web 應(yīng)用程序時,您總是會編寫 url 路由。 每條路線都是 Web 應(yīng)用程序中的特定頁面。 打開頁面 ??/about?
?? 可能會調(diào)用 ??about_page() ?
?方法。
@app.route("/about") def about_page(): return "Website about nachos"
將參數(shù)傳遞給 Python 裝飾器
到目前為止,您已經(jīng)學(xué)習(xí)了如何創(chuàng)建一些有用的 Python 裝飾器。然而,這些裝飾器都沒有傳入?yún)?shù)。在本節(jié)中,您將學(xué)習(xí)如何創(chuàng)建接受參數(shù)的 Python 裝飾器。
為此,我們將允許在 Python 語法魔術(shù)解壓縮。使用 ??func_name(*args,**kwargs)?
?,它將解壓縮所有參數(shù)和所有關(guān)鍵字參數(shù)。通過在裝飾器中使用它,可以確保裝飾器將接受任意數(shù)量的參數(shù)或關(guān)鍵字參數(shù)。這使得它們在重復(fù)使用時更加實用。
def print_function_name(func): def inner(*args, **kwargs): print(f'Running {func.__name__}...') return func(*args, **kwargs) return inner @print_function_name def add_nums(a, b): print(a + b) add_nums(1, 2) # Running add_nums... # 3
上述方法的美妙之處在于它同時接受位置和關(guān)鍵字參數(shù)。因此,即使我們以以下任何格式執(zhí)行該函數(shù),該函數(shù)也將運行:
- ?
?add_nums(1024, 2020)?
? - ?
?add_nums(1024, b = 2021)?
? - ?
?add_nums(a = 1024, b = 2222)?
?
使用多個 Python 裝飾器
關(guān)于 Python 裝飾器的一個有趣的方式是:可以同時使用多個裝飾器。這意味著您可以將多個裝飾器應(yīng)用于單個函數(shù)。為了理解這一點,來看一個例子:
def one(func): def inner(*args, **kwargs): print('1') return func(*args, **kwargs) return inner def two(func): def inner(*args, **kwargs): print('2') return func(*args, **kwargs) return inner @one @two def speak(text): print(text) speak('Hello') # 1 # 2 # Hello
我們的裝飾器函數(shù)所做的唯一事情就是打印出數(shù)字 1 和數(shù)字 2。通過將裝飾器 ??@one?
?? 放在 ??@two?
?? 之前,您可以將 ??two()?
??包裝的函數(shù)包裝為 ??one()?
?。為了說明這一點,您可以切換順序以查看如何修改行為:
# Changing decorator order @two @one def speak(text): print(text) speak('Hello') # 2 # 1 # Hello
通過首先放置 ??@two?
? 裝飾器,該函數(shù)成為最外層的函數(shù)。
總結(jié)
在本文中,我們先了解了什么是 Python 裝飾器,它代表元編程的語法糖。 Python 裝飾器允許我們修改函數(shù)行為并允許我們以不同的方式擴展函數(shù)。
接著了解裝飾器是什么以及如何使用它們。學(xué)習(xí)了如何允許將參數(shù)傳遞給 Python 裝飾器,學(xué)習(xí)了幾個常見的裝飾器有用的工具。最后,如何使用多個裝飾器以及裝飾器的先后順序。
參考文章:
- ??Primer on Python Decorators – Real Python??
- ??Python Decorators Introduction - Python Tutorial (pythonbasics.org)?
到此這篇關(guān)于Python中裝飾器的文章就介紹到這了,更多相關(guān)Python裝飾器內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
python調(diào)用matplotlib模塊繪制柱狀圖
這篇文章主要為大家介紹了python調(diào)用matplotlib模塊繪制柱狀圖,文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下2019-10-10Django自定義插件實現(xiàn)網(wǎng)站登錄驗證碼功能
這篇文章主要為大家詳細介紹了Django自定義插件實現(xiàn)網(wǎng)站登錄驗證碼功能,具有一定的參考價值,感興趣的小伙伴們可以參考一下2017-04-04