一、函數(shù)的定義
在 Python 中為了將代碼的流程進行分解,可以通過函數(shù)對程序代碼的邏輯進行過程化(函數(shù)是面向過程的)和結構化(一個函數(shù)是一個獨立的處理結構)的封裝,將整塊具有獨立功能的代碼隔離并打包到一個單獨的函數(shù)中,從而將一個系統(tǒng)分割為不同的部分。
我們可以把具有一定功能性(可以完成某項任務)的代碼放到函數(shù)中,這樣在程序中需要此函數(shù)的功能時就可以直接調用函數(shù),而不必進行重復的代碼拷貝 —— 這樣既能節(jié)省空間,也有助于保持程序的一致性,因為當后期有修改需要時你只需改變此函數(shù)的代碼而無須去尋找修改大量代碼的拷貝。
函數(shù)是Python為了最大化的代碼重用和最小化的代碼冗余而提供的最基本的程序結構。
1、函數(shù)的定義
在某些編程語言(比如C/C++)里,函數(shù)的聲明和定義是區(qū)分開的。一個函數(shù)聲明包括函數(shù)名和參數(shù)的名字(傳統(tǒng)上還有參數(shù)的類型和函數(shù)的類型也就是返回值的類型), 但不必給出函數(shù)的任何代碼, 具體的代碼通常屬于函數(shù)定義的范疇。這樣的設計往往是為了將函數(shù)的定義和聲明放在不同的文件中。 Python將這兩者視為一體,函數(shù)語句由聲明的標題行以及隨后的定義體組成。
我們使用 def 關鍵字來自定義函數(shù)。自定義的函數(shù)主要由函數(shù)頭和可執(zhí)行的函數(shù)體兩部分組成。
函數(shù)定義可以由一個或多個裝飾器表達式包裝。裝飾器表達式是在函數(shù)定義時,在包含函數(shù)定義的作用域中的計算。結果必須是可調用的,它以函數(shù)對象作為唯一的參數(shù)來調用。返回的值綁定到函數(shù)名稱而不是函數(shù)對象。多個裝飾器以嵌套方式應用,關于裝飾器的原理和應用我們會在下面的章節(jié)詳細介紹。
下面的代碼:2、函數(shù)的返回值
Python 里的函數(shù)使用 return 語句返回一個對象,返回的對象可以是一個容器類型,比如序列和字典。如果函數(shù)中沒有 return 語句, 就會自動返回 None 對象。如果函數(shù)返回多個對象,python 會自動把他們包裝在一個元組中來返回。
許多靜態(tài)類型的語言主張一個函數(shù)的類型就是其返回值的類型。但是在 python 中, 由于 python 是動態(tài)地確定類型而且函數(shù)能返回不同類型的對象,所以沒有將函數(shù)和類型進行直接的的關聯(lián)。
3、函數(shù)對象
def 是一個可執(zhí)行語句。當 Python 解釋器運行了 def 語句后,便創(chuàng)建了一個函數(shù)對象(函數(shù)的可執(zhí)行代碼的包裝器)并將其賦值給了 def 關鍵字后面緊跟的變量名。就像所有的賦值一樣,函數(shù)名變成了這個函數(shù)對象的引用。函數(shù)對象可以賦值給其他的變量名,甚至可以保存在列表之中。函數(shù)定義時不執(zhí)行函數(shù)體只是生成函數(shù)對象并對其進行賦值,只有當函數(shù)被調用時函數(shù)對象(函數(shù)體)才被執(zhí)行。
在典型的操作中,def 語句一般在模塊文件中編寫,并自然而然的在模塊文件第一次被導入的時候生成定義的函數(shù)對象。
4、函數(shù)屬性
可以向函數(shù)附加任意的用戶定義的屬性。這樣的屬性可以用來直接把狀態(tài)信息附加到函數(shù)對象,而不必使用全局、非本地和類等其他技術。和非本地不同,這樣的屬性可以在函數(shù)自身的任何地方訪問。這種變量的名稱對于一個函數(shù)來說是本地的,但是,其值在函數(shù)退出后仍然保留。屬性與對象相關而不是與作用域相關,但直接效果是類似的。
5、函數(shù)注解
在 Python 3 中可以給函數(shù)對象附加注解信息 —— 與函數(shù)的參數(shù)和結果相關的任意的用戶自定義的數(shù)據。Python為聲明注解提供了特殊的語法,但是,它自身不做任何事情。注解完全是可選的,并且,出現(xiàn)的時候只是直接附加到函數(shù)對象的 __annotations__ 屬性以供其他用戶使用。
參數(shù)可以在參數(shù)名稱后面帶有 “: expression” 形式的注解。任何參數(shù)都可以具有注解,即使是 *args 或 \kwargs 形式。函數(shù)可以在參數(shù)列表的后面帶有 “ -> expression ” 形式的 “返回值” 注解。這些注解可以是任何有效的 Python 表達式,并且在執(zhí)行函數(shù)定義時計算。注解可能以不同于它們在源代碼中出現(xiàn)的順序計算。注解的存在不改變函數(shù)的語義。注解的值可以通過函數(shù)對象的 __annotations__ 字典屬性訪問,以參數(shù)的名稱作為鍵。
你可以在函數(shù)頭部的各部分之間使用空格,也可以不用,但省略他們對某些讀者來說可能會提高代碼的可讀性。
注解可以用作參數(shù)類型或值的特定限制,并且較大的API中你可以使用這一功能作為標識函數(shù)接口信息的方式。
6、匿名函數(shù):lambda
除了 def 語句之外,Python 還提供了一種生成函數(shù)對象的表達式形式:lambda 。
這個表達式創(chuàng)建了一個能夠調用的函數(shù),但是它返回了一個函數(shù)對象而不是將這個函數(shù)對象賦值給一個變量名。這也就是 lambda 有時叫做匿名(也就是沒有函數(shù)名)函數(shù)的原因。
lambda的一般形式是:關鍵字 lambda,之后是一個或多個參數(shù)(相當于 def 語句頭部內用括號括起來的參數(shù)列表),緊跟著的是一個冒號,之后是一個表達式,這個表達式的定義體必須和聲明放在同一行。參數(shù)是可選的,如果使用的參數(shù)話,參數(shù)通常也是表達式的一部分。
lambda [argument1, argument2, .... argumentn]: expression
由 lambda 表達式所返回的函數(shù)對象與由 def 創(chuàng)建并賦值后的函數(shù)對象工作起來是完全一樣的,但是 lambda 有一些不同之處讓其在扮演特定的角色時很有用:
lambda 是一個表達式,而不是一個語句
lambda 表達式能夠出現(xiàn)在 Python 語句語法上不允許 def 出現(xiàn)的地方。例如:在一個列表常量中或者函數(shù)調用的參數(shù)中。此外作為一個表達式,lambda 返回了一個值(一個新的函數(shù)對象),可以選擇性地賦值給一個變量名。相反,def 語句總是得在頭部將一個新的函數(shù)賦值給一個變量名,而不是將這個函數(shù)作為結果返回。
lambda 的主體是一個單個的表達式或語句,而不是一個代碼塊
lambda 通常要比 def 功能要小:你僅能夠在 lambda 主體中封裝有限的邏輯進去,連 if 這樣的語句都不能夠使用。這是有意設計的——它限制了程序的嵌套:lambda 是一個為編寫簡單的函數(shù)而設計的,而 def 用來處理更大的任務。
為什么使用 lambda
通常來說,lambda 起到了一種函數(shù)速寫的作用,允許在使用的代碼內嵌入一個函數(shù)的定義。它們完全是可選的(你總是能夠使用 def 來替代它們),但是在你僅需要嵌入小段可執(zhí)行代碼的情況下它們會帶來一個更簡潔的代碼結構。
lambda 通常用來編寫跳轉表,也就是行為的列表或字典,能夠按照需要執(zhí)行相應的動作。我們可以用 Python 中的字典或者其他的數(shù)據結構來構建更多種類的行為表,從而做同樣的事情。
7、內部/內嵌函數(shù)
在函數(shù)體內創(chuàng)建另外一個函數(shù)(對象)是完全合法的。這種函數(shù)叫做內部/內嵌函數(shù)。因為現(xiàn)在 python 支持靜態(tài)地嵌套域,內部函數(shù)實際上很有用的。
最明顯的創(chuàng)造內部函數(shù)的方法是在外部函數(shù)的定義體內定義函數(shù)。8、遞歸函數(shù)
Python 支持遞歸函數(shù) —— 即直接或間接的調用自身以進行循環(huán)的函數(shù)。它允許程序遍歷擁有任意的、不可預知的形狀的結構。遞歸甚至是簡單循環(huán)和迭代的替換,盡管它不一定是最簡單的或最高效的一種。循環(huán) VS 遞歸
遞歸在 Python 中并不像 Prolog 或 Lisp 這樣更加深奧的語言中那樣常用,因為Python 強調像循環(huán)這樣的簡單的過程式語句,循環(huán)語句通常更為自然。處理任意結構
遞歸可以要求遍歷任意形狀的結構。簡單的循環(huán)語句在這里不起作用,因為這不是一個線性迭代。嵌套的循環(huán)語句也不夠用,因為子列表可能嵌套到任意的深度并且以任意的形式嵌套。相反,下面的代碼使用遞歸來對應這種一般性的嵌套,以便順序訪問子列表。9、函數(shù)的設計理念
耦合性:只有在真正必要的情況下使用全局變量。
全局變量通常是一種蹩腳的函數(shù)間進行通信的辦法。它們引發(fā)了依賴關系和計時的問題,會導致程序調試和修改的困難。
耦合性:不要改變可改變類型的參數(shù),除非調用者希望這樣做。
函數(shù)可以改變傳入的可變類型對象,但是就像全局變量一樣,這回導致很多調用者和被調用者之間的耦合性,這種耦合性會導致一個函數(shù)過于特殊和不友好。
耦合性:避免直接改變在另一個模塊文件中的變量。
改變引入模塊中的變量會導致模塊文件間的耦合性,就像全局變量產生了函數(shù)間的耦合一樣:模塊難于理解和重用。
聚合性:每一個函數(shù)都應該有一個單一的、統(tǒng)一的目標。
在設計完美的情況下,每一個函數(shù)中都應該做一件事:這件事可以用一個簡單說明句來總結。
簡潔:每一個函數(shù)都應該盡可能的簡潔。
Python 代碼是以簡單明了而著稱的 ,一個過長或者有著深層嵌套的函數(shù)往往就成為設計缺陷的征兆。保持簡單,保持簡短。
通常來講,我們應該竭力使函數(shù)和其他編程組件中的外部依賴性最小化。函數(shù)的自包含性越好,它越容易被理解、復用和修改。
10、和函數(shù)相關的內建函數(shù)
filter()
函數(shù)式編程的意思就是對序列應用一些函數(shù)的工具。例如,基于某一測試函數(shù)過濾出一些元素(filter),以及對每隊元素都應用函數(shù)并運行到最后結果(reduce)。map()
程序對列表和其他序列常常要做的一件事情就是對每一個元素進行一個操作并把其結果集合起來。因為 map() 是內置函數(shù),它總是可用的,并總是以同樣的方式工作,還有一些性能方面的優(yōu)勢(它要比自己編寫的 for 循環(huán)更快)。
reduce()
reduce() 位于 functools 模塊中,要更復雜一些。它接收一個迭代器來處理,但是,它自身不是一個迭代器,它返回一個單個的結果。《Python基礎手冊》系列:
Python基礎手冊 1 —— Python語言介紹
Python基礎手冊 2 —— Python 環(huán)境搭建(Linux)
Python基礎手冊 3 —— Python解釋器
Python基礎手冊 4 —— 文本結構
Python基礎手冊 5 —— 標識符和關鍵字
Python基礎手冊 6 —— 操作符
Python基礎手冊 7 —— 內建函數(shù)
Python基礎手冊 8 —— Python對象
Python基礎手冊 9 —— 數(shù)字類型
Python基礎手冊10 —— 序列(字符串)
Python基礎手冊11 —— 序列(元組&列表)
Python基礎手冊12 —— 序列(類型操作)
Python基礎手冊13 —— 映射(字典)
Python基礎手冊14 —— 集合
Python基礎手冊15 —— 解析
Python基礎手冊16 —— 文件
Python基礎手冊17 —— 簡單語句
Python基礎手冊18 —— 復合語句(流程控制語句)
Python基礎手冊19 —— 迭代器
Python基礎手冊20 —— 生成器
Python基礎手冊21 —— 函數(shù)的定義
Python基礎手冊22 —— 函數(shù)的參數(shù)
Python基礎手冊23 —— 函數(shù)的調用
Python基礎手冊24 —— 函數(shù)中變量的作用域
Python基礎手冊25 —— 裝飾器
Python基礎手冊26 —— 錯誤 & 異常
Python基礎手冊27 —— 模塊
Python基礎手冊28 —— 模塊的高級概念
Python基礎手冊29 —— 包