Python基礎(chǔ)手冊21——函數(shù)的定義

一虐译、函數(shù)的定義

在 Python 中為了將代碼的流程進(jìn)行分解,可以通過函數(shù)對程序代碼的邏輯進(jìn)行過程化(函數(shù)是面向過程的)和結(jié)構(gòu)化(一個函數(shù)是一個獨(dú)立的處理結(jié)構(gòu))的封裝鞋囊,將整塊具有獨(dú)立功能的代碼隔離并打包到一個單獨(dú)的函數(shù)中宴偿,從而將一個系統(tǒng)分割為不同的部分。

我們可以把具有一定功能性(可以完成某項(xiàng)任務(wù))的代碼放到函數(shù)中钟沛,這樣在程序中需要此函數(shù)的功能時(shí)就可以直接調(diào)用函數(shù),而不必進(jìn)行重復(fù)的代碼拷貝 —— 這樣既能節(jié)省空間局扶,也有助于保持程序的一致性恨统,因?yàn)楫?dāng)后期有修改需要時(shí)你只需改變此函數(shù)的代碼而無須去尋找修改大量代碼的拷貝。

函數(shù)是Python為了最大化的代碼重用和最小化的代碼冗余而提供的最基本的程序結(jié)構(gòu)三妈。


1畜埋、函數(shù)的定義

在某些編程語言(比如C/C++)里,函數(shù)的聲明和定義是區(qū)分開的畴蒲。一個函數(shù)聲明包括函數(shù)名和參數(shù)的名字(傳統(tǒng)上還有參數(shù)的類型和函數(shù)的類型也就是返回值的類型)悠鞍, 但不必給出函數(shù)的任何代碼, 具體的代碼通常屬于函數(shù)定義的范疇模燥。這樣的設(shè)計(jì)往往是為了將函數(shù)的定義和聲明放在不同的文件中咖祭。 Python將這兩者視為一體,函數(shù)語句由聲明的標(biāo)題行以及隨后的定義體組成蔫骂。

我們使用 def 關(guān)鍵字來自定義函數(shù)么翰。自定義的函數(shù)主要由函數(shù)頭和可執(zhí)行的函數(shù)體兩部分組成。

函數(shù)頭(首行)由 def 關(guān)鍵字以及緊隨其后的函數(shù)名再加上放在小括號中的參數(shù)(可選)組成辽旋。首行語句由一個冒號(:)結(jié)束硬鞍,后面僅跟著一個可選(強(qiáng)烈推薦)的文檔字串,和必需的代碼塊戴已。這個代碼塊通常都會縮進(jìn)(或者就是緊跟在冒號后邊的簡單的一句)固该。而這個代碼塊就是函數(shù)的主體 —— 也就是每當(dāng)調(diào)用函數(shù)時(shí) Python 所執(zhí)行的語句。

函數(shù)定義可以由一個或多個裝飾器表達(dá)式包裝糖儡。裝飾器表達(dá)式是在函數(shù)定義時(shí)伐坏,在包含函數(shù)定義的作用域中的計(jì)算。結(jié)果必須是可調(diào)用的握联,它以函數(shù)對象作為唯一的參數(shù)來調(diào)用桦沉。返回的值綁定到函數(shù)名稱而不是函數(shù)對象。多個裝飾器以嵌套方式應(yīng)用金闽,關(guān)于裝飾器的原理和應(yīng)用我們會在下面的章節(jié)詳細(xì)介紹纯露。

下面的代碼:

大致相當(dāng)于:

函數(shù)一般會使用參數(shù)作為輸入,使用return作為輸出代芜,關(guān)于參數(shù)的詳細(xì)說明我們會在下面的章節(jié)具體介紹埠褪。函數(shù)具體的輸入輸出方式可以參考下面的圖示:


2、函數(shù)的返回值

Python 里的函數(shù)使用 return 語句返回一個對象挤庇,返回的對象可以是一個容器類型钞速,比如序列和字典。如果函數(shù)中沒有 return 語句嫡秕, 就會自動返回 None 對象渴语。如果函數(shù)返回多個對象,python 會自動把他們包裝在一個元組中來返回昆咽。

return 語句會直接終止函數(shù)的執(zhí)行并返回后面的對象驾凶,所以 return 后面的代碼不會執(zhí)行。

許多靜態(tài)類型的語言主張一個函數(shù)的類型就是其返回值的類型掷酗。但是在 python 中调违, 由于 python 是動態(tài)地確定類型而且函數(shù)能返回不同類型的對象,所以沒有將函數(shù)和類型進(jìn)行直接的的關(guān)聯(lián)汇在。


3翰萨、函數(shù)對象

def 是一個可執(zhí)行語句。當(dāng) Python 解釋器運(yùn)行了 def 語句后糕殉,便創(chuàng)建了一個函數(shù)對象(函數(shù)的可執(zhí)行代碼的包裝器)并將其賦值給了 def 關(guān)鍵字后面緊跟的變量名亩鬼。就像所有的賦值一樣,函數(shù)名變成了這個函數(shù)對象的引用阿蝶。函數(shù)對象可以賦值給其他的變量名雳锋,甚至可以保存在列表之中。

我們可以把函數(shù)對象填入到數(shù)據(jù)結(jié)構(gòu)中羡洁,就好像它們是整數(shù)或字符串一樣玷过。Python的復(fù)合類型可以包含任意類型的對象。

函數(shù)定義時(shí)不執(zhí)行函數(shù)體只是生成函數(shù)對象并對其進(jìn)行賦值,只有當(dāng)函數(shù)被調(diào)用時(shí)函數(shù)對象(函數(shù)體)才被執(zhí)行辛蚊。

在典型的操作中粤蝎,def 語句一般在模塊文件中編寫,并自然而然的在模塊文件第一次被導(dǎo)入的時(shí)候生成定義的函數(shù)對象袋马。


4初澎、函數(shù)屬性

可以向函數(shù)附加任意的用戶定義的屬性。

這樣的屬性可以用來直接把狀態(tài)信息附加到函數(shù)對象虑凛,而不必使用全局碑宴、非本地和類等其他技術(shù)。和非本地不同桑谍,這樣的屬性可以在函數(shù)自身的任何地方訪問延柠。這種變量的名稱對于一個函數(shù)來說是本地的,但是锣披,其值在函數(shù)退出后仍然保留贞间。屬性與對象相關(guān)而不是與作用域相關(guān),但直接效果是類似的盈罐。


5榜跌、函數(shù)注解

在 Python 3 中可以給函數(shù)對象附加注解信息 —— 與函數(shù)的參數(shù)和結(jié)果相關(guān)的任意的用戶自定義的數(shù)據(jù)。Python為聲明注解提供了特殊的語法盅粪,但是钓葫,它自身不做任何事情。注解完全是可選的票顾,并且础浮,出現(xiàn)的時(shí)候只是直接附加到函數(shù)對象的 __annotations__ 屬性以供其他用戶使用。

參數(shù)可以在參數(shù)名稱后面帶有 “: expression” 形式的注解奠骄。任何參數(shù)都可以具有注解豆同,即使是 *args 或 \kwargs 形式。函數(shù)可以在參數(shù)列表的后面帶有 “ -> expression ” 形式的 “返回值” 注解含鳞。這些注解可以是任何有效的 Python 表達(dá)式影锈,并且在執(zhí)行函數(shù)定義時(shí)計(jì)算。注解可能以不同于它們在源代碼中出現(xiàn)的順序計(jì)算蝉绷。注解的存在不改變函數(shù)的語義鸭廷。注解的值可以通過函數(shù)對象的 __annotations__ 字典屬性訪問,以參數(shù)的名稱作為鍵熔吗。

調(diào)用一個注解過的函數(shù)辆床,像以前一樣,不過桅狠,當(dāng)注解出現(xiàn)的時(shí)候讼载,Python 將他們收集到字典中并且將它們附加給函數(shù)對象自身轿秧。參數(shù)名編程鍵,如果寫了返回值注解的話咨堤,它存儲在鍵 "return" 下菇篡,而注解的值則賦給了注解表達(dá)式的結(jié)果。

由于注解只是附加到一個 Python 對象的 Python 對象吱型,注解可以直接處理逸贾。

如果編寫了注解的話漱竖,仍然可以對參數(shù)使用默認(rèn)值 —— 注解出現(xiàn)在默認(rèn)值之前擎厢。

你可以在函數(shù)頭部的各部分之間使用空格豆混,也可以不用,但省略他們對某些讀者來說可能會提高代碼的可讀性触徐。

注解可以用作參數(shù)類型或值的特定限制,并且較大的API中你可以使用這一功能作為標(biāo)識函數(shù)接口信息的方式狐赡。


6撞鹉、匿名函數(shù):lambda

除了 def 語句之外,Python 還提供了一種生成函數(shù)對象的表達(dá)式形式:lambda 颖侄。

這個表達(dá)式創(chuàng)建了一個能夠調(diào)用的函數(shù)鸟雏,但是它返回了一個函數(shù)對象而不是將這個函數(shù)對象賦值給一個變量名。這也就是 lambda 有時(shí)叫做匿名(也就是沒有函數(shù)名)函數(shù)的原因览祖。

lambda的一般形式是:關(guān)鍵字 lambda孝鹊,之后是一個或多個參數(shù)(相當(dāng)于 def 語句頭部內(nèi)用括號括起來的參數(shù)列表),緊跟著的是一個冒號展蒂,之后是一個表達(dá)式又活,這個表達(dá)式的定義體必須和聲明放在同一行。參數(shù)是可選的锰悼,如果使用的參數(shù)話柳骄,參數(shù)通常也是表達(dá)式的一部分。

lambda [argument1, argument2, .... argumentn]: expression

由 lambda 表達(dá)式所返回的函數(shù)對象與由 def 創(chuàng)建并賦值后的函數(shù)對象工作起來是完全一樣的箕般,但是 lambda 有一些不同之處讓其在扮演特定的角色時(shí)很有用:

lambda 是一個表達(dá)式耐薯,而不是一個語句
lambda 表達(dá)式能夠出現(xiàn)在 Python 語句語法上不允許 def 出現(xiàn)的地方。例如:在一個列表常量中或者函數(shù)調(diào)用的參數(shù)中丝里。此外作為一個表達(dá)式曲初,lambda 返回了一個值(一個新的函數(shù)對象),可以選擇性地賦值給一個變量名丙者。相反复斥,def 語句總是得在頭部將一個新的函數(shù)賦值給一個變量名,而不是將這個函數(shù)作為結(jié)果返回械媒。

lambda 的主體是一個單個的表達(dá)式或語句目锭,而不是一個代碼塊
lambda 通常要比 def 功能要衅捞:你僅能夠在 lambda 主體中封裝有限的邏輯進(jìn)去,連 if 這樣的語句都不能夠使用痢虹。這是有意設(shè)計(jì)的——它限制了程序的嵌套:lambda 是一個為編寫簡單的函數(shù)而設(shè)計(jì)的被去,而 def 用來處理更大的任務(wù)。

除了這些差別奖唯,def 和 lambda 都能夠做同樣種類的工作惨缆。


為什么使用 lambda

通常來說,lambda 起到了一種函數(shù)速寫的作用丰捷,允許在使用的代碼內(nèi)嵌入一個函數(shù)的定義坯墨。它們完全是可選的(你總是能夠使用 def 來替代它們),但是在你僅需要嵌入小段可執(zhí)行代碼的情況下它們會帶來一個更簡潔的代碼結(jié)構(gòu)病往。

lambda 通常用來編寫跳轉(zhuǎn)表捣染,也就是行為的列表或字典,能夠按照需要執(zhí)行相應(yīng)的動作停巷。

當(dāng)需要把小段的可執(zhí)行代碼編寫進(jìn) def 語句從語法上不能編寫進(jìn)的地方時(shí)耍攘,lambda 表達(dá)式作為 def 的一種速寫來說是最為有用的。一個 def 是不會在列表常量中工作的畔勤,因?yàn)樗且粋€語句蕾各,而不是一個表達(dá)式。

我們可以用 Python 中的字典或者其他的數(shù)據(jù)結(jié)構(gòu)來構(gòu)建更多種類的行為表庆揪,從而做同樣的事情式曲。


7、內(nèi)部/內(nèi)嵌函數(shù)

在函數(shù)體內(nèi)創(chuàng)建另外一個函數(shù)(對象)是完全合法的嚷硫。這種函數(shù)叫做內(nèi)部/內(nèi)嵌函數(shù)检访。因?yàn)楝F(xiàn)在 python 支持靜態(tài)地嵌套域,內(nèi)部函數(shù)實(shí)際上很有用的仔掸。

最明顯的創(chuàng)造內(nèi)部函數(shù)的方法是在外部函數(shù)的定義體內(nèi)定義函數(shù)脆贵。


8、遞歸函數(shù)

Python 支持遞歸函數(shù) —— 即直接或間接的調(diào)用自身以進(jìn)行循環(huán)的函數(shù)起暮。它允許程序遍歷擁有任意的卖氨、不可預(yù)知的形狀的結(jié)構(gòu)。遞歸甚至是簡單循環(huán)和迭代的替換负懦,盡管它不一定是最簡單的或最高效的一種筒捺。


循環(huán) VS 遞歸

遞歸在 Python 中并不像 Prolog 或 Lisp 這樣更加深奧的語言中那樣常用,因?yàn)镻ython 強(qiáng)調(diào)像循環(huán)這樣的簡單的過程式語句纸厉,循環(huán)語句通常更為自然系吭。

而且 for 循環(huán)為我們自動迭代,使得遞歸在大多數(shù)情況下不必使用(并且颗品,很可能肯尺,遞歸在內(nèi)存空間和執(zhí)行時(shí)間方面效率更低)沃缘。


處理任意結(jié)構(gòu)

遞歸可以要求遍歷任意形狀的結(jié)構(gòu)。簡單的循環(huán)語句在這里不起作用则吟,因?yàn)檫@不是一個線性迭代槐臀。嵌套的循環(huán)語句也不夠用,因?yàn)樽恿斜砜赡芮短椎饺我獾纳疃炔⑶乙匀我獾男问角短酌ブ佟O喾此旅娴拇a使用遞歸來對應(yīng)這種一般性的嵌套,以便順序訪問子列表敬扛。


9晰洒、函數(shù)的設(shè)計(jì)理念

耦合性:只有在真正必要的情況下使用全局變量。

全局變量通常是一種蹩腳的函數(shù)間進(jìn)行通信的辦法舔哪。它們引發(fā)了依賴關(guān)系和計(jì)時(shí)的問題欢顷,會導(dǎo)致程序調(diào)試和修改的困難。

耦合性:不要改變可改變類型的參數(shù)捉蚤,除非調(diào)用者希望這樣做。

函數(shù)可以改變傳入的可變類型對象炼七,但是就像全局變量一樣缆巧,這回導(dǎo)致很多調(diào)用者和被調(diào)用者之間的耦合性,這種耦合性會導(dǎo)致一個函數(shù)過于特殊和不友好豌拙。

耦合性:避免直接改變在另一個模塊文件中的變量陕悬。

改變引入模塊中的變量會導(dǎo)致模塊文件間的耦合性,就像全局變量產(chǎn)生了函數(shù)間的耦合一樣:模塊難于理解和重用按傅。

聚合性:每一個函數(shù)都應(yīng)該有一個單一的捉超、統(tǒng)一的目標(biāo)。

在設(shè)計(jì)完美的情況下唯绍,每一個函數(shù)中都應(yīng)該做一件事:這件事可以用一個簡單說明句來總結(jié)拼岳。

簡潔:每一個函數(shù)都應(yīng)該盡可能的簡潔。

Python 代碼是以簡單明了而著稱的 况芒,一個過長或者有著深層嵌套的函數(shù)往往就成為設(shè)計(jì)缺陷的征兆惜纸。保持簡單,保持簡短绝骚。

通常來講耐版,我們應(yīng)該竭力使函數(shù)和其他編程組件中的外部依賴性最小化。函數(shù)的自包含性越好压汪,它越容易被理解粪牲、復(fù)用和修改。


10止剖、和函數(shù)相關(guān)的內(nèi)建函數(shù)

filter()

函數(shù)式編程的意思就是對序列應(yīng)用一些函數(shù)的工具腺阳。例如落君,基于某一測試函數(shù)過濾出一些元素(filter),以及對每隊(duì)元素都應(yīng)用函數(shù)并運(yùn)行到最后結(jié)果(reduce)舌狗。


map()

程序?qū)α斜砗推渌蛄谐3R龅囊患虑榫褪菍γ恳粋€元素進(jìn)行一個操作并把其結(jié)果集合起來叽奥。

map() 函數(shù)會對一個序列對象中的每一個元素應(yīng)用被傳入的函數(shù),并且返回一個包含了所有函數(shù)調(diào)用結(jié)果的一個列表痛侍。

因?yàn)?map() 是內(nèi)置函數(shù)朝氓,它總是可用的,并總是以同樣的方式工作主届,還有一些性能方面的優(yōu)勢(它要比自己編寫的 for 循環(huán)更快)赵哲。


reduce()

reduce() 位于 functools 模塊中,要更復(fù)雜一些君丁。它接收一個迭代器來處理枫夺,但是,它自身不是一個迭代器绘闷,它返回一個單個的結(jié)果橡庞。


《Python基礎(chǔ)手冊》系列:

Python基礎(chǔ)手冊 1 —— Python語言介紹
Python基礎(chǔ)手冊 2 —— Python 環(huán)境搭建(Linux)
Python基礎(chǔ)手冊 3 —— Python解釋器
Python基礎(chǔ)手冊 4 —— 文本結(jié)構(gòu)
Python基礎(chǔ)手冊 5 —— 標(biāo)識符和關(guān)鍵字
Python基礎(chǔ)手冊 6 —— 操作符
Python基礎(chǔ)手冊 7 —— 內(nèi)建函數(shù)
Python基礎(chǔ)手冊 8 —— Python對象
Python基礎(chǔ)手冊 9 —— 數(shù)字類型
Python基礎(chǔ)手冊10 —— 序列(字符串)
Python基礎(chǔ)手冊11 —— 序列(元組&列表)
Python基礎(chǔ)手冊12 —— 序列(類型操作)
Python基礎(chǔ)手冊13 —— 映射(字典)
Python基礎(chǔ)手冊14 —— 集合
Python基礎(chǔ)手冊15 —— 解析
Python基礎(chǔ)手冊16 —— 文件
Python基礎(chǔ)手冊17 —— 簡單語句
Python基礎(chǔ)手冊18 —— 復(fù)合語句(流程控制語句)
Python基礎(chǔ)手冊19 —— 迭代器
Python基礎(chǔ)手冊20 —— 生成器
Python基礎(chǔ)手冊21 —— 函數(shù)的定義
Python基礎(chǔ)手冊22 —— 函數(shù)的參數(shù)
Python基礎(chǔ)手冊23 —— 函數(shù)的調(diào)用
Python基礎(chǔ)手冊24 —— 函數(shù)中變量的作用域
Python基礎(chǔ)手冊25 —— 裝飾器
Python基礎(chǔ)手冊26 —— 錯誤 & 異常
Python基礎(chǔ)手冊27 —— 模塊
Python基礎(chǔ)手冊28 —— 模塊的高級概念
Python基礎(chǔ)手冊29 —— 包

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市印蔗,隨后出現(xiàn)的幾起案子扒最,更是在濱河造成了極大的恐慌,老刑警劉巖华嘹,帶你破解...
    沈念sama閱讀 206,214評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件吧趣,死亡現(xiàn)場離奇詭異,居然都是意外死亡耙厚,警方通過查閱死者的電腦和手機(jī)强挫,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,307評論 2 382
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來薛躬,“玉大人俯渤,你說我怎么就攤上這事》汉溃” “怎么了稠诲?”我有些...
    開封第一講書人閱讀 152,543評論 0 341
  • 文/不壞的土叔 我叫張陵,是天一觀的道長诡曙。 經(jīng)常有香客問我臀叙,道長,這世上最難降的妖魔是什么价卤? 我笑而不...
    開封第一講書人閱讀 55,221評論 1 279
  • 正文 為了忘掉前任劝萤,我火速辦了婚禮,結(jié)果婚禮上慎璧,老公的妹妹穿的比我還像新娘床嫌。我一直安慰自己跨释,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,224評論 5 371
  • 文/花漫 我一把揭開白布厌处。 她就那樣靜靜地躺著鳖谈,像睡著了一般。 火紅的嫁衣襯著肌膚如雪阔涉。 梳的紋絲不亂的頭發(fā)上缆娃,一...
    開封第一講書人閱讀 49,007評論 1 284
  • 那天,我揣著相機(jī)與錄音瑰排,去河邊找鬼贯要。 笑死,一個胖子當(dāng)著我的面吹牛椭住,可吹牛的內(nèi)容都是我干的崇渗。 我是一名探鬼主播,決...
    沈念sama閱讀 38,313評論 3 399
  • 文/蒼蘭香墨 我猛地睜開眼京郑,長吁一口氣:“原來是場噩夢啊……” “哼宅广!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起些举,我...
    開封第一講書人閱讀 36,956評論 0 259
  • 序言:老撾萬榮一對情侶失蹤乘碑,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后金拒,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,441評論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡套腹,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,925評論 2 323
  • 正文 我和宋清朗相戀三年绪抛,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片电禀。...
    茶點(diǎn)故事閱讀 38,018評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡幢码,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出尖飞,到底是詐尸還是另有隱情症副,我是刑警寧澤,帶...
    沈念sama閱讀 33,685評論 4 322
  • 正文 年R本政府宣布政基,位于F島的核電站贞铣,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏沮明。R本人自食惡果不足惜辕坝,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,234評論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望荐健。 院中可真熱鬧酱畅,春花似錦琳袄、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,240評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至餐蔬,卻和暖如春碎紊,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背用含。 一陣腳步聲響...
    開封第一講書人閱讀 31,464評論 1 261
  • 我被黑心中介騙來泰國打工矮慕, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人啄骇。 一個月前我還...
    沈念sama閱讀 45,467評論 2 352
  • 正文 我出身青樓痴鳄,卻偏偏與公主長得像,于是被迫代替她去往敵國和親缸夹。 傳聞我的和親對象是個殘疾皇子痪寻,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,762評論 2 345

推薦閱讀更多精彩內(nèi)容