最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

python?自定義裝飾器使用及原理詳解(最新推薦)

 更新時間:2025年02月27日 08:57:06   作者:1_bit  
本文詳細介紹了Python裝飾器的原理和使用方法,包括簡單的裝飾器、帶參數(shù)的裝飾器、原函數(shù)的傳參、保留原函數(shù)元數(shù)據(jù)以及類裝飾器,通過這些講解,讀者可以全面了解裝飾器的強大功能和應(yīng)用技巧,感興趣的朋友一起看看吧

注意:先行知識 python,本篇文章所有代碼均為實際運行,為原理和邏輯講解

一、裝飾器

裝飾器是 python 中的一種語法糖,雖然我不想用語法糖這個詞來表達,但這句話寫在了開頭,我也不到用別的更準確的詞來形容他了。

如果你剛接觸編程不久,不理解語法糖,也沒關(guān)系;在這里我說語法糖并不會影響到你接下來的理解,我只是用它在贅述,作為了一種形容詞。

首先我們要搞懂裝飾器是什么東西,其次搞懂裝飾器的基礎(chǔ)知識點,最后逐漸加深即可非常熟練的使用裝飾器。

其實,你可以將裝飾器當作是 python 自帶的函數(shù)功能增效器,使函數(shù)可以額外的增加功能,但并不影響被增加功能函數(shù)本身(雖然有辦法影響,但一般不會這樣去做)。就像是一個插座,本身這個插座只允許兩個頭的插孔電器,但這個插座有一個功能,有一個連通器可以額外增加不同的插座接口,就這樣把不同功能的插座進行連接,這樣就實現(xiàn)了額外的功能,但這個插座本身卻不會發(fā)生改變。

二、簡單的裝飾器

咱們的裝飾器學習一步步進行,先來看一個最簡單的裝飾器:

@app.route("/")
def home():
    return "Hello World!"

@app.route("/") 這是一個在 flask 中的路由裝飾器,在這里與 home 函數(shù)進行綁定,在檢測到訪問當前站點的根目錄時,即會執(zhí)行 home 函數(shù)。

而在這里,裝飾器的作用則是給了當前的 home 函數(shù)一個額外的功能,使其能夠響應(yīng) web 請求。

那么咱們開始編寫一個自定義裝飾器:

def simple_decorator(func):
    def wrapper():
        print("Before function call")
        result = func()
        print("After function call")
        return result
    return wrapper

以上是一個簡單的自定義裝飾器的實現(xiàn),咱們可以得知,一個簡單的嵌套函數(shù)形式。咱們解構(gòu)一下裝飾器的語法,得到以下結(jié)果:

def 裝飾器名稱(func):       # 接收一個函數(shù)(比如 home)
    def wrapper():        # 包裝原函數(shù)
        # 添加自定義的新功能(比如權(quán)限檢查)
        func()            # 執(zhí)行原函數(shù)
    return wrapper

我們從以上的語法中得知,裝飾器最外層的函數(shù),為一個接收一個函數(shù)作為參數(shù)的函數(shù),其內(nèi)部用一個 wrapper 為名函數(shù)包裝原函數(shù) func(),并且在此處 func() 執(zhí)行原函數(shù);最后返回整個函數(shù) wrapper。

你可以理解為一個函數(shù)使用了裝飾器如下:

@decorator
def func(): 
    pass

隨后會轉(zhuǎn)變成:

func = decorator(func)

你再這里可能有疑問,在這里一定要使用 wrapper 來包裹(包裝)原函數(shù) func() 嗎?當然不,在這里 wrapper 只是一個約定俗成的名字,但推薦用 wrapper 為名的函數(shù)來包裹 func() 原函數(shù)的執(zhí)行,因為可能某些第三方的工具、庫 之類會采用這個約定俗稱的方式作為依賴,說不定會影響到某些功能的實現(xiàn),并且 func 也是約定俗稱代表著原函數(shù)。

其實通過以上的簡單了解,此時應(yīng)該知曉,基礎(chǔ)的自定義裝飾器其實就是一個二次包裹的一個函數(shù),外層函數(shù)接收原函數(shù)為參數(shù),二層函數(shù)則包裹這個原函數(shù)進行執(zhí)行,最后返回包裹原函數(shù)的函數(shù)即可。

三、裝飾器詳解

通過第二點我們可以發(fā)現(xiàn),例子中沒有對應(yīng)的傳遞參數(shù)給裝飾器的功能,那當我們需要對裝飾器進行傳參時該怎么做呢?

其實很簡單,咱們只需要在原本的兩層包裹的函數(shù)外再加上一層函數(shù)接收參數(shù)即可:

def out_decorator(param):
    def in_decorator(func):      # 第二層:接收函數(shù)
        def wrapper():           # 第三層:包裝函數(shù)
            print(param)
            func()
        return wrapper
    return in_decorator
@out_decorator("裝飾器傳參")  # 傳遞參數(shù)
def home():
    print("原本函數(shù)內(nèi)容")
home()  # 輸出:裝飾器傳參 → 原本函數(shù)內(nèi)容

再上面的函數(shù)中,out_decorator 是一個新增接收參數(shù)的裝飾器,當然也可以接收多個,其內(nèi)部的兩層包裹的函數(shù)與之前第二次所闡述的簡單裝飾器一樣,使用 in_decorator 接收了 func 原函數(shù)后,在 in_decorator 函數(shù)內(nèi)部再使用 wrapper 對原函數(shù)進行包裹即可,最后返回包裹函數(shù) wrapper。

四、原函數(shù)的傳參

既然說到了裝飾器傳參,那原函數(shù)的傳參我們該怎么做呢?

在此我們使用一種無需維護的傳參方式,為了使一些可能對python 接觸不深的同學閱讀,在此回對這種方式進行一下解釋。

4.1 位置參數(shù)

在正式介紹傳參前,需要了解一下函數(shù)參數(shù)的兩種參數(shù)寫法,首先是位置參數(shù)。

咱們在對函數(shù)進行傳參時可能會這樣 func(1, 2, 3);此時我們將 func 的參數(shù) 1 會說成傳給 func 的第一個參數(shù)為 1,那么第二個參數(shù)為2,第三個參數(shù)為 3… 這樣以此類推,我們使用位置對這些參數(shù)進行語言上的引導與定位,這種傳參方式的參數(shù)即為位置參數(shù)。

4.2 關(guān)鍵字參數(shù)

在傳參時不僅可以使用 func(1, 2, 3) 這樣的位置參數(shù),還可以使用 func(name="John", age=20) 這樣的傳參方式。

func(name="John", age=20) 的傳參方式指定了參數(shù)所對應(yīng)的“名”,就例如給與了一個鍵值對的方式,這種方式不再使用位置信息作為引導指定,我們會說給與 func 函數(shù)的參數(shù)有 2 個,一個是name 另一個是 age,name 的值為 john,age 的值為 20;這種傳參方式的參數(shù)即為關(guān)鍵字參數(shù)。

4.3 *args 與 ?**kwargs 的打包

在python 中,有兩個特殊的符號表示不同參數(shù),其中 *args 表示位置參數(shù),**kwargs 表示 關(guān)鍵字參數(shù)。

在這里,若讀者沒有深究,可能覺得 *args 與 ?**kwargs 就是關(guān)鍵字的存在,其實,在這里真正起作用的是 * 和 **,而 args 、kwargs 則是約定俗稱的名稱,args 表示位置參數(shù)、kwargs 表示關(guān)鍵字參數(shù)(先這樣理解,但在不同情況下有其他解釋,這只是當說命名上。)

在 python 中 * 與 ** 為python中的可變參數(shù),主要用于對數(shù)據(jù)進行打包、解包。

我們先演示打包吧,以下是一個例子:

def func(a, *args, ?**kwargs):
    print("a:", a)
    print("args:", args)    # 額外位置參數(shù) → 元組
    print("kwargs:", kwargs) # 關(guān)鍵字參數(shù) → 字典

當我們傳入?yún)?shù) func(1, 2, 3, name=“John”, age=20) 時,位置參數(shù)1則是個與到 func 中的參數(shù) a,2與3則直接被 *args進行了打包,成為一個元組 (2, 3) 給與到變量 args,而 name=“John”, age=20 關(guān)鍵字變量,則被 **kwargs 打包給與到 kwargs。

其結(jié)果輸出為:

# 輸出:
# a: 1
# args: (2, 3)
# kwargs: {'name': 'John', 'age': 20}

其中參數(shù) a, *args, ?**kwargs :

  • *args 是一個 * 號則表示把傳入過來的位置參數(shù)打包成元組給與到自身
  • **kwargs 是兩個 *號則表示 把傳入過來的關(guān)鍵字參數(shù)打包成字典給與到自身

不要認為這是指針,這是 python 中的可變參數(shù)的語法*args 和 **kwargs 只是參數(shù)處理方式,不涉及內(nèi)存地址操作。python是高級語言,所有變量都是對對象的引用?(類似指針的抽象概念),但用戶無法直接操作內(nèi)存地址。

總結(jié)一下:

  • 在函數(shù)定義中 * 和 ** 用于接收任意數(shù)量的參數(shù)進行打包接收

4.4 *args 與 ?**kwargs 的解包

以下是一個演示 * 和 ** 的解包代碼:

def add(a, b):
    return a + b
numbers = (1, 2)
add(*numbers)  # 等價于 add(1, 2) → 3
params = {"a": 3, "b": 4}
add(**params)  # 等價于 add(a=3, b=4) → 7

以上代碼在對元組 numbers = (1, 2) 進行解包后 add(*numbers) 傳遞參數(shù),此時的元組參數(shù)變成了單獨的數(shù)據(jù)進行傳參,此時參數(shù)為位置參數(shù)。

以上代碼在對字典 params = {“a”: 3, “b”: 4} 進行解包后 add(**params) 傳遞參數(shù),此時的元組參數(shù)變成了單獨的key、val 數(shù)據(jù)進行傳參,此時參數(shù)為關(guān)鍵字參數(shù)參數(shù)。

總結(jié)一下:

  • 在函數(shù)調(diào)用中 * 和 ** 用于解包參數(shù)(將序列或字典展開為單獨參數(shù))

4.5 原函數(shù)傳參實現(xiàn)

通過以上對 *args 與 ?kwargs 后,已經(jīng)知曉如何接收所有的位置參數(shù)與關(guān)鍵字參數(shù),那么此時我們只需要在包裝原函數(shù)的 wrapper 函數(shù)寫上 *args, ?kwargs,再原樣給與到 func 函數(shù)參數(shù)即可:

def decorator(func):
    def wrapper(*args, ?**kwargs):  # 接收所有參數(shù)
        print("裝飾器邏輯")
        return func(*args, ?**kwargs)  # 原樣傳遞參數(shù)給原函數(shù)
    return wrapper

可能有些同學會問,不給 wrapper 寫 *args, ?**kwargs 不可以嗎?其實裝飾器的核心是替換原函數(shù),當調(diào)用被裝飾的函數(shù)時,實際上調(diào)用的是wrapper函數(shù)。

因此,wrapper需要能夠接收所有傳遞給原函數(shù)的參數(shù),無論是位置參數(shù)還是關(guān)鍵字參數(shù)。如果wrapper不接受任何參數(shù),當被裝飾的函數(shù)被調(diào)用時,傳遞的參數(shù)就會丟失,導致錯誤。

若你的裝飾器也需要接收參數(shù),那么再嵌套一層函數(shù)即可:

def out_decorator(param):
	def in_decorator(func):
	    def wrapper(*args, ?**kwargs):  # 接收所有參數(shù)
	        print("裝飾器邏輯")
	        return func(*args, ?**kwargs)  # 原樣傳遞參數(shù)給原函數(shù)
	    return wrapper
	return in_decorator    

4.6 保留原函數(shù)元數(shù)據(jù)

若有一個函數(shù)使用了裝飾器:

def decorator(func):
    def wrapper(*args, ?**kwargs):
        return func(*args, ?**kwargs)
    return wrapper
@decorator
def home():
    print("Hello")
print(home.__name__)   # 輸出:wrapper

此時當你調(diào)用當前 home 函數(shù)的元數(shù)據(jù)函數(shù)名時,會直接輸出 wrapper 這個包裹原函數(shù)的函數(shù)名。

在上一節(jié)說過裝飾器的核心是替換原函數(shù),當調(diào)用被裝飾的函數(shù)時,實際上調(diào)用的是wrapper函數(shù)。

那么如何才能讓裝飾器不干擾原函數(shù),使其假裝起到“裝飾”作用,假裝不更改其內(nèi)部元數(shù)據(jù)呢?此時只需要添加 @wraps(func) 即可。

注意 @wraps(func) 需要引入 from functools import wraps。

修改代碼為:

from functools import wraps
def decorator(func):
	@wraps(func)
    def wrapper(*args, ?**kwargs):
        return func(*args, ?**kwargs)
    return wrapper
@decorator
def home():
    print("Hello")
print(home.__name__)   # 輸出:wrapper

如果是裝飾器需要接收參數(shù),也同理添加即可在 wrapper 之前即可:

def out_decorator(param):
	def in_decorator(func):
		@wraps(func)
	    def wrapper(*args, ?**kwargs):  # 接收所有參數(shù)
	        print("裝飾器邏輯")
	        return func(*args, ?**kwargs)  # 原樣傳遞參數(shù)給原函數(shù)
	    return wrapper
	return in_decorator    

在這里建議,除非你有特殊需求,其他情況建議都加上 @wraps(func)。

五、類裝飾器

裝飾器不止可以使用函數(shù)實現(xiàn),還可以使用類,代碼如下:

class ClassDecorator:
    def __init__(self, func):
        self.func = func
    def __call__(self, *args, ?**kwargs):
        print("裝飾器邏輯前")
        result = self.func(*args, ?**kwargs)
        print("裝飾器邏輯后")
        return result
@ClassDecorator
def my_function():
    print("原函數(shù)邏輯")
my_function()
# 輸出:
# 裝飾器邏輯前
# 原函數(shù)邏輯
# 裝飾器邏輯后

__call__ 方法承擔了類似 wrapper 的角色,在 Python 中,__call__ 是一個特殊方法,它允許類的實例像函數(shù)一樣被調(diào)用,從而執(zhí)行 __call__ 中的代碼,這與之前所說的在調(diào)用被裝飾的函數(shù)實際上是執(zhí)行了 wrapper 一樣,則當類裝飾器中使用 __call__ 時,它的核心作用是讓裝飾后的函數(shù)調(diào)用邏輯被包裝在類的方法中。

到此這篇關(guān)于python 自定義裝飾器使用及原理詳解的文章就介紹到這了,更多相關(guān)python 自定義裝飾器使用內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • Python操作Excel文件的11種方法(全網(wǎng)最全)

    Python操作Excel文件的11種方法(全網(wǎng)最全)

    在日常工作或開發(fā)過程中,Excel文件作為一種常用的數(shù)據(jù)存儲格式,其高效便捷的數(shù)據(jù)處理能力被廣泛應(yīng)用于數(shù)據(jù)統(tǒng)計、數(shù)據(jù)分析等領(lǐng)域,Python作為一種強大的編程語言,提供了豐富的庫支持來實現(xiàn)對Excel文件的操作,本篇將詳細介紹如何使用Python來操作Excel文件
    2025-03-03
  • 2021年值得向Python開發(fā)者推薦的VS Code擴展插件

    2021年值得向Python開發(fā)者推薦的VS Code擴展插件

    這篇文章主要介紹了2021年值得向Python開發(fā)者推薦的VS Code擴展插件,幫助大家更好的利用vscode進行python的開發(fā),感興趣的朋友可以了解下
    2021-01-01
  • Django中間件Middleware功能詳解

    Django中間件Middleware功能詳解

    Django中間件(Middleware)是Django框架中的一個功能,它允許開發(fā)者在處理請求和響應(yīng)的過程中插入自定義代碼,中間件能夠在視圖函數(shù)執(zhí)行前后進行操作,本文給大家介紹Django中間件Middleware功能,感興趣的朋友一起看看吧
    2024-10-10
  • 在Python中使用sort()方法進行排序的簡單教程

    在Python中使用sort()方法進行排序的簡單教程

    這篇文章主要介紹了在Python中使用sort()方法進行排序的簡單教程,是Python學習中的基礎(chǔ)知識,需要的朋友可以參考下
    2015-05-05
  • Python3安裝與環(huán)境配置的超完整教程

    Python3安裝與環(huán)境配置的超完整教程

    這篇文章主要介紹了在Windows系統(tǒng)中安裝和配置Python3,包括下載、安裝、環(huán)境變量配置、驗證安裝、安裝常用工具和庫以及解決常見問題,需要的朋友可以參考下
    2025-02-02
  • Python 調(diào)用 zabbix api的方法示例

    Python 調(diào)用 zabbix api的方法示例

    這篇文章主要介紹了Python 調(diào)用 zabbix api的方法示例,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2019-01-01
  • 關(guān)于Django使用 django-celery-beat動態(tài)添加定時任務(wù)的方法

    關(guān)于Django使用 django-celery-beat動態(tài)添加定時任務(wù)的方法

    本文給大家介紹Django使用 django-celery-beat動態(tài)添加定時任務(wù)的方法,安裝對應(yīng)的是celery版本,文中給大家介紹的非常詳細,對大家的學習或工作具有一定的參考借鑒價值,需要的朋友參考下吧
    2021-10-10
  • selenium執(zhí)行js并繞過webdriver監(jiān)測常見方法

    selenium執(zhí)行js并繞過webdriver監(jiān)測常見方法

    這篇文章主要為大家介紹了selenium執(zhí)行js并繞過webdriver監(jiān)測常見方法,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步早日升職加薪
    2022-04-04
  • OpenCV圖像變換之傅里葉變換的一些應(yīng)用

    OpenCV圖像變換之傅里葉變換的一些應(yīng)用

    這篇文章主要給大家介紹了關(guān)于OpenCV圖像變換之傅里葉變換的相關(guān)資料,傅里葉變換可以將一幅圖片分解為正弦和余弦兩個分量,換而言之,他可以將一幅圖像從其空間域(spatial domain)轉(zhuǎn)換為頻域(frequency domain),需要的朋友可以參考下
    2021-07-07
  • 手把手教你用python搶票回家過年(代碼簡單)

    手把手教你用python搶票回家過年(代碼簡單)

    下面給大家分享一個使用Python寫一個命令行版的火車票查看器, 只要在命令行敲一行命令就能獲得你想要的火車票信息,具體實現(xiàn)代碼大家參考下本文
    2018-01-01

最新評論

霍城县| 克山县| 彝良县| 富裕县| 富锦市| 孙吴县| 调兵山市| 济源市| 招远市| 仙居县| 获嘉县| 鱼台县| 桐城市| 宣化县| 永昌县| 永福县| 福建省| 汉川市| 讷河市| 陇西县| 永善县| 余江县| 苗栗县| 清水县| 即墨市| 临江市| 宜宾市| 柘荣县| 宁夏| 大港区| 大邑县| 辽源市| 万源市| 余姚市| 南充市| 元氏县| 阳谷县| 镇原县| 石楼县| 宜城市| 措勤县|