第八章 函式(下)

weixin_52846494發表於2020-12-05

第八章 函式(下)

8.1 間接呼叫函式
前面呼叫函式時,使用函式名加引數列表的形式呼叫。除此之外,還可以將函式名賦值給一個變數,再通過變數名加引數列表的形式間接呼叫函式,如例所示。
在這裡插入圖片描述
大家可能會疑惑:間接呼叫函式有何用處?這種用法可以使一個函式作為另一個函式的引數,如例所示。
在這裡插入圖片描述
另外,函式名還可以作為其他資料型別的元素,如例所示。
在這裡插入圖片描述
8.2 匿名函式
匿名函式是指沒有函式名稱的、臨時使用的微函式。它可以通過lambda表示式來宣告,其語法格式如下:
在這裡插入圖片描述
其中,“[arg1 [, arg2, …, argn]]”表示函式的引數,“表示式”表示函式體。lambda表示式只可以包含一個表示式,其計算結果可以看作是函式的返回值。雖然lambda表示式不允許包含其他複雜的語句,但在表示式中可以呼叫其他函式。
接下來演示lambda表示式的使用,如例所示。
在這裡插入圖片描述
在例中,第1行使用lambda表示式宣告匿名函式並賦值給sum,相等於這個函式有了函式名sum,該行相當於以下程式碼:在這裡插入圖片描述
使用lambda表示式宣告的匿名函式也可以作為自定義函式的實參,如例所示。在這裡插入圖片描述
此外,lambda表示式宣告的匿名函式還可以作為內建函式的實參,如例所示。在這裡插入圖片描述
lambda表示式表示一個匿名函式,也可以作為列表或字典的元素,如例所示。在這裡插入圖片描述
8.3 閉包
在前面章節中,函式可以通過return返回一個變數。此外,函式也可以返回另外一個函式名,如例所示。在這裡插入圖片描述
此外,還可以將f1()函式的定義移動到f2()函式中,這樣f2()函式外的作用域就不能直接呼叫f1()函式,如例所示。在這裡插入圖片描述
將一個函式的定義巢狀到另一個函式中,還有其他的作用,如例所示。在這裡插入圖片描述
在例中,函式f2()中傳入一個引數,在函式f1()中對該引數中的元素求和,具體執行過程如圖所示。在這裡插入圖片描述
在上圖中,list1作為引數傳進函式f2()中,此時不能把函式f1()移到函式f2()的外面。因為函式f1()的功能是計算list中所有元素值的和,所以f1()函式必須依賴於函式f2()的引數。如果函式f1()在函式f2()外,則無法取得f2()中的資料進行計算,這就引出了閉包的概念。
如果內層函式引用了外層函式的變數(包括其引數),並且外層函式返回內層函式名,這種函式架構稱為閉包。從概念中可以得出,閉包需要滿足的三個條件:
內層函式的定義巢狀在外層函式中。
內層函式引用外層函式的變數。
外層函式返回內層函式名。

8.4 裝飾器
在夏天天氣晴朗時,人們通常只穿T恤就可以了,但當颳風下雨時,人們通常在T恤的基礎上再增加一件外套,它可以遮風擋雨,並且不影響T恤原有的作用,這就是現實生活中裝飾器的概念。
8.4.1 裝飾器的概念
裝飾器本質上還是函式,可以讓其他函式在不做任何程式碼修改的前提下增加額外功能。它通常用於有切面需求的場景,例如,插入日誌、效能測試、許可權校驗等。
在講解裝飾器之前,先看一段簡單的程式,如例所示。在這裡插入圖片描述
執行結果如圖所示。在這裡插入圖片描述
在例中, 第1行定義了一個帶單個引數func的名稱為 f2的函式,第2行f1()函式為閉包的功能函式,其中呼叫了func()函式並將func()函式的返回值加1並返回。這樣每次f2()函式被呼叫時,func的值可能會不同,但不論func()代表何種函式,程式都將呼叫它。
從程式執行結果可看出,呼叫函式decorated()的返回值為2,呼叫func()函式的返回值為1,兩者都輸出“func()函式”,此時稱變數decorated是func的裝飾版,即在func()函式的基礎上增加新功能,本例是將func()函式的返回值加1。

還可以用裝飾版來代替“func”,這樣每次呼叫時就總能得到“附帶其他功能”的func版本,如例所示。在這裡插入圖片描述
通過上例可以得出裝飾器的概念,即一個以函式作為引數並返回一個替換函式的可執行函式。裝飾器的本質是一個巢狀函式,外層函式的引數是被修飾的函式,內層函式是一個閉包並在其中增加新功能(裝飾器的功能函式)。

還可以用裝飾版來代替“func”,這樣每次呼叫時就總能得到“附帶其他功能”的func版本,如例所示。在這裡插入圖片描述
通過上例可以得出裝飾器的概念,即一個以函式作為引數並返回一個替換函式的可執行函式。裝飾器的本質是一個巢狀函式,外層函式的引數是被修飾的函式,內層函式是一個閉包並在其中增加新功能(裝飾器的功能函式)。
8.4.2 @符號的應用
上例中使用變數名將裝飾器函式與被裝飾函式聯絡起來。此外,還可以通過@符號和裝飾器名實現兩者的聯絡,如例所示。在這裡插入圖片描述
在例中,第5行通過@符號和裝飾器名實現裝飾器函式與被裝飾函式聯絡。第9行呼叫func()函式時,程式會自動呼叫裝飾器函式的程式碼。
8.4.3 裝飾有引數的函式
裝飾器除了可以裝飾無引數的函式外,還可以裝飾有引數的函式,如例所示。在這裡插入圖片描述
在例中,第6行定義一個帶有兩個預設引數的func()函式。第5行將f2()函式宣告為裝飾器函式,用來修飾func()函式。第9行呼叫func裝飾器函式,注意f1()函式中的引數必須包含對應func()函式的引數。
8.4.4 帶引數的裝飾器——裝飾器工廠
通過上面的學習,裝飾器本身也是一個函式,即裝飾器本身也可以帶引數,此時裝飾器需要再多一層內嵌函式,如例所示。在這裡插入圖片描述
若大家不理解上面程式碼,可以將裝飾器寫成如下程式碼,如例所示。在這裡插入圖片描述
在例中,將裝飾器分解成閉包的巢狀,這種寫法更容易理解。此外,還可以將第11、12行程式碼寫成如下程式碼:在這裡插入圖片描述
上述程式碼相當於省略中間變數f2。

8.5 偏函式
函式最重要的一個功能的是複用程式碼,有時在複用已有函式時,可能需要固定其中的部分引數,除了設定預設值引數外,還可以使用偏函式(用來固定函式呼叫時部分或全部引數的函式叫偏函式),如例所示。在這裡插入圖片描述
在例中,第3行定義一個myAdd2()函式,與第1行myAdd1()函式的區別僅在於引數c固定為一個數字123,這時就可以使用偏函式來複用上面的函式。

8.6 常用的內建函式
在Python中,內建函式是被自動載入的,可以隨時呼叫這些函式,不需要定義,極大地簡化了程式設計。
8.6.1 eval()函式
eval()函式用於對動態表示式求值,其語法格式如下:在這裡插入圖片描述
其中,source是動態表示式的字串,globals和locals是求值時使用的上下文環境的全域性變數和區域性變數,如果不指定,則使用當前執行上下文。
接下來演示eval()函式的用法,如例所示。在這裡插入圖片描述
在例中,通過input()函式輸入Python表示式,接著通過eval()函式求出該表示式的值。
8.6.2 exec()函式
exec()函式用於動態語句的執行,其語法格式如下:在這裡插入圖片描述
其中,source是動態語句的字串,globals和locals是使用的上下文環境的全域性變數和區域性變數,如果不指定,則使用當前執行上下文。
接下來演示exec()函式的用法,如例所示。在這裡插入圖片描述
在例中,通過input()函式輸入Python語句,接著通過exec()函式執行該語句。8.6.3 compile()函式
compile()函式用於將一個字串編譯為位元組程式碼,其語法格式如下:在這裡插入圖片描述
其中,source為程式碼語句的字串,filename為程式碼檔名稱,如果不是從檔案讀取程式碼,則傳遞一些可辨認的值,mode為指定編譯程式碼的種類,其值可以為’exec’、‘eval’、‘single’,剩餘引數一般使用預設值。
接下來演示compile()函式的用法,如例所示。在這裡插入圖片描述
在例中,通過input()函式輸入Python語句,接著通過compile()函式將字串str轉換為位元組程式碼物件。
8.6.4 map()函式
程式中經常需要對列表和其他序列中的每個元素進行同一個操作並把其結果集合起來,具體示例如下:在這裡插入圖片描述
上述程式碼表示將list1中的每個元素加1並新增到list2中。該程式執行後,輸出結果如下:在這裡插入圖片描述
實際上,Python提供了一個更方便的工具來完成此種操作,這就是map()函式,其語法格式如下:在這裡插入圖片描述
其中,function為函式名,其餘引數為序列,返回值為迭代器物件,通過list()函式可以將其轉換為列表,也可以使用for迴圈進行遍歷操作。

接下來演示map()函式的用法,如例所示。在這裡插入圖片描述
在例中,map()函式對列表list1中的每個元素呼叫func函式並將返回結果組成一個可迭代物件,如圖所示。在這裡插入圖片描述
此外,map()函式還可以接受兩個序列,具體示例如下:在這裡插入圖片描述
該程式執行後,輸出結果如下:在這裡插入圖片描述
8.6.5 filter()函式
filter()函式可以對指定序列進行過濾操作,其語法格式如下:在這裡插入圖片描述
其中,function為函式名,它所引用的函式只能接受一個引數,並且返回值是布林值,sequence為一個序列,filter()函式返回值為迭代器物件。
接下來演示filter()函式的用法,如例所示。在這裡插入圖片描述
在例中,filter()函式對列表list中的每個元素呼叫func函式並返回使得func函式返回值為True的元素組成的可迭代物件,如圖所示。在這裡插入圖片描述
8.6.6 zip()函式
zip()函式用於將一系列可迭代的物件作為引數,將物件中對應的元素打包成一個個元組,然後返回由這些元組組成的迭代物件,如例所示。在這裡插入圖片描述
在例中,zip()函式將列表list1中第1個元素與列表list2中的1個元素組成一個元組,以此類推,最終返回由3個元組組成的迭代物件。
zip()引數可以接受任何型別的序列,同時也可以有兩個以上的引數。但當傳入引數的長度不同時,zip()函式以最短序列長度為準進行擷取獲得元組,具體示例如下:在這裡插入圖片描述
該程式執行後,輸出結果如下:在這裡插入圖片描述
此外,在zip()函式中還可以使用運算子,如例所示。
在這裡插入圖片描述
在例中,第3行zip()函式中使用
運算子相當於執行相反的操作。
在Python中,還有許多內建函式,當要用到某個函式時,只需在PyCharm編輯器中寫出函式名,它就會自動提示函式的引數。例如,在編輯器中輸入map後出現如圖所示的提示。 在這裡插入圖片描述
此時在編輯器中接著輸入(),則會提示函式的每個引數型別,如圖所示。在這裡插入圖片描述
8.7 小案例
8.7.1 案例
假設已實現使用者聊天、購買商品、顯示個人資訊等功能,在使用這些功能前需驗證使用者使用的登陸方式(微信、QQ或其他)及身份資訊,要求使用裝飾器實現該功能。
具體實現如例所示。
在這裡插入圖片描述
本章小結
本章主要介紹了Python中函式的高階用法,包括間接呼叫函式、匿名函式、閉包、裝飾器、偏函式及常用的內建函式。通過本章的學習,應理解閉包及裝飾器的用法並應用到實際開發中。

相關文章