摘要:那么,這個裝飾器要怎么定義呢我們來看一下。當然了,如果大家在網絡上搜索,關于如何定義裝飾器,看到的是一個更加規(guī)范的版本。在當中,調用原函數時又,即把輸入的元祖解包再傳入。
我們在編程過程中,常常會遇到這種需求:
比如,我想開發(fā)一款計算器,我已經寫好了一堆函數,用于執(zhí)行各種計算,那么我們需要在執(zhí)行各種計算函數前,首先對輸入的數據進行檢查,確保他們必須得是數值才允許執(zhí)行函數,而不能是字符串;
又如,我想編寫一個用于計算三角形周長、面積、某個角角度的模塊,已經寫好幾個函數用于計算,那么,在執(zhí)行計算前,首先要確保輸入的三條邊長能夠構成三角形,再進行計算才有意義;
再比如,我想開發(fā)某款網絡應用,寫了一些函數用于實現用戶的某些操作,那么,得要先檢查確認該用戶已經登錄了,才允許執(zhí)行這些操作。
這些需求,歸納起來,就是,在執(zhí)行主函數之前,常常要先執(zhí)行某個預函數,進行一些校驗之類的操作。
這類需求是非常常見的,也是保證程序完整性、健壯性的重要舉措。所以,怎么做才比較簡單呢?
你會說,這很簡單啊,在每個函數里面寫上if語句不就得了。就拿那個計算器而言,如果我們要寫加減乘除,我們可以這樣:
def plus(a,b): if type(a)==type(0) and type(b)==type(0): #假設該計算器只能計算整數,如果要計算小數再or type(0.0) return a+b else: print("Type must be number") #檢測到數據類型不對,先輸出報警,函數值返回None return Nonedef minus(a,b): if type(a)==type(0) and type(b)==type(0): return a-b else: print("Type must be number") return Nonedef multiply(a,b): if type(a)==type(0) and type(b)==type(0): return a*b else: print("Type must be number") return Nonedef divide(a,b): if type(a)==type(0) and type(b)==type(0): return a/b else: print("Type must be number") return None
這個嘛,直接暴力。但是呢,這里只有4個函數,假如你開發(fā)的計算器有幾十幾百個函數,每個函數都要套上if語句,這不得麻煩死了,不煩死也啰嗦死了。
所以怎么弄簡單一點呢?聰明的你肯定想到了,我們可以把那個判斷if也多帶帶定義一個函數,然后把計算用的函數套在里面,就像這樣:
def check(a,b,func): #定義檢查函數,變量為待檢測參數a,b和檢測通過后執(zhí)行的函數func if type(a)==type(0) and type(b)==type(0): return func(a,b) else: print("Type must be number") return Nonedef plus(a,b): return a+bdef minus(a,b): return a-b...#主程序check(1,2,plus) #計算1+2check(1,2,minus) #計算1-2check(1,2,multiply) #計算1*2check(1,2,divide) #計算1/2
這里面有一點一定要特別注意,主程序的check(1,2,plus) 是把plus函數本身作為變量傳遞給check,由check函數決定如何執(zhí)行plus函數,此處不能寫成check(1,2,plus(1,2)),plus不能帶參數和括號,不是執(zhí)行plus()后把結果傳給check。
這么寫程序簡潔了不少,加減乘除函數只需要定義他們本身的運算就可以了,變量檢測交給了check函數。這么寫也是比較容易理解的。
但是對于使用該程序的用戶來說,就不是這么回事了,他們會覺得這么寫非常難看。
為什么呢?我是要拿程序做加減乘除計算的,但我不論計算什么,每次都是在主調用check這個函數!
那有沒有什么辦法,可以既好看,又簡潔呢?裝飾器就是起到了這個神奇的作用。
上面這個需求,用裝飾器可以這么寫:
def check(func): ...@checkdef plus(a,b): return a+b@checkdef minus(a,b): return a-b...#主程序plus(1,2) #計算1+2minus(1,2) #計算1-2...
先直觀感受一下,通過@check,check函數就被“注入”到了plus函數中,使得plus函數擁有了參數檢測的功能。這樣,在主程序中,若要計算加法就可直接調用plus,便可先校驗再計算。
那么,這個裝飾器check要怎么定義呢?我們來看一下。
def check(func): #定義裝飾器check def newfunc(a,b): #定義函數模板,即如何處理func if type(a)==type(0) and type(b)==type(0): return func(a,b) else: print("Type must be number!") return None return newfunc #將處理后的func作為新函數newfunc輸出@checkdef plus(a,b): return a+b#主程序,計算1+2plus(1,2)
我們可以看到,當裝飾器@check作用于plus函數時,plus函數本身作為參數func傳入裝飾器中。在裝飾器check的定義內部,定義了一個函數模板,描述了對輸入的func如何處理??梢钥吹?,newfunc對func(也就是輸入的plus)套用了判斷數據類型的if語句,最后,再將套好的newfunc輸出,替代原來的func。這樣,此時執(zhí)行func就是在執(zhí)行newfunc,執(zhí)行plus就是在執(zhí)行套上if語句的新函數。
所以,通過裝飾器,添加上了判斷語句的新函數替換了原來的plus函數,但仍通過plus這個函數名調用,所以看起來就是plus函數被“裝飾”了。
當然了,如果大家在網絡上搜索,關于如何定義裝飾器,看到的是一個更加規(guī)范的版本??雌饋砀y理解一些,但其實是一樣的:
def checkall(func): def wrapper(*args,**kwargs): if type(args[0])==type(0) and type(args[1])==type(0): return func(*args,**kwargs) else: print("Type must be number!") return None return wrapper
模板函數一般習慣用wrapper來表示,這個沒啥,建議大家都這么寫,規(guī)范一些。
參數一般用不定長的*args,**kwargs來表示,這個可能有些人就困惑了。因為被裝飾的函數可能有很多種,參數的個數一般也不確定。然后*args,**kwargs是什么東西?args,kwargs這兩個形參英文字母是什么無所謂可以自己定,關鍵是前面的單星號*和雙星號**。
假如我定義一個函數,不能確定參數有多少個,例如要對輸入的一組數字做連加操作。那么就可以定義plus(*x),當調用該函數時,若輸入多個變量plus(1,2,3),那么就會把輸入的變量組合成一個元祖x=(1,2,3)輸入。定義雙星號plus(**x)的意思是,調用該函數時若寫出形參變量plus(a=1,b=2,c=3),那么輸入變量就會組合成字典x={a:1,b:2,c:3}傳入函數。
當然也可以反向操作,定義函數的時候參數個數是明確的plus(a,b,c),那么調用該函數時,加上星號plus(*(1,2,3)),就是對輸入元祖(1,2,3)執(zhí)行炸開操作,轉換為plus(1,2,3)輸入。
裝飾器里這么寫有什么用呢?我們仔細觀察一下我們之前寫的newfunc(a,b),那就意味著,指明了新函數有兩個參數a,b,假如被裝飾的原函數有三個參數怎么辦呢?不就沒用了嗎?
我們來看別人寫的,定義時用了wrapper(*args,**kwargs),即不管有多少個參數,打包輸入wrapper。在wrapper當中,調用原函數時又func(*args,**kwargs),即把輸入的元祖解包再傳入func。這么一打包一解包,雖然看起來啥都沒干,但確適應了函數參數不確定的情況,使得該裝飾器可以裝飾多種參數數量不同的函數。
就先這樣吧。
歡迎轉載、收藏、有所收獲點贊支持一下!
目前開通了技術交流群,群友已超過2000人,添加時最好的備注方式為:來源+興趣方向,方便找到志同道合的朋友
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規(guī)行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://systransis.cn/yun/124520.html
摘要:今天就結合最近的世界杯帶大家理解下裝飾器。而德國是上屆的冠軍,又是這屆奪冠熱門。裝飾器的存在是為了適用兩個場景,一個是增強被裝飾函數的行為,另一個是代碼重用。在利用語法糖,簡化賦值操作。行為良好的裝飾器可以重用,以減少代碼量。 Python 裝飾器是在面試過程高頻被問到的問題,裝飾器也是一個非常好用的特性,熟練掌握裝飾器會讓你的編程思路更加寬廣,程序也更加 pythonic。 show...
摘要:我們以測量函數運行時間為例來講一講裝飾器的運行原理。三更加通用的裝飾器前面兩部分講了裝飾器的原理,這一部分就講講要寫出一個通用的裝飾器需要注意的問題。首先就是參數的問題,裝飾器返回的函數不是原來的函數,函數的簽名也就和原來的函數簽名不一樣。 一、最簡單的裝飾器 裝飾器是python中很基礎也很實用的一個特性。通過裝飾器我們可以很方便地為一些函數添加相同的功能。我們以測量函數運行時間為例...
摘要:使用類裝飾器,優(yōu)點是靈活性大,高內聚,封裝性。不過不用擔心,有,本身也是一個裝飾器,它的作用就是把原函數的元信息拷貝到裝飾器函數中,使得裝飾器函數也有和原函數一樣的元信息。 showImg(https://segmentfault.com/img/bVbrFWb?w=742&h=484);Python的裝飾器(decorator)是一個很棒的機制,也是熟練運用Python的必殺技之一。...
摘要:裝飾器是可調用的對象,其參數是另一個函數被裝飾的函數。第二大特性是,裝飾器在加載模塊時立即執(zhí)行。另一個常見的裝飾器是,它的作用是協助構建行為良好的裝飾器。 裝飾器是可調用的對象,其參數是另一個函數(被裝飾的函數)。 裝飾器基礎知識 首先看一下這段代碼 def deco(fn): print I am %s! % fn.__name__ @deco def func(): ...
閱讀 1035·2021-11-23 09:51
閱讀 2359·2021-10-08 10:22
閱讀 2653·2021-09-29 09:35
閱讀 871·2021-09-22 15:20
閱讀 2872·2019-08-30 15:53
閱讀 2422·2019-08-30 13:55
閱讀 1110·2019-08-29 17:27
閱讀 2879·2019-08-29 17:26