Python基礎(chǔ)手冊22——函數(shù)的參數(shù)

二、函數(shù)的參數(shù)

在 Python 中艳悔,兩種類型的參數(shù):在函數(shù)定義時(shí)函數(shù)名后面的括號(hào)中定義的參數(shù)急凰,我們稱之為 形參;在函數(shù)調(diào)用時(shí),函數(shù)名后面的括號(hào)中傳遞的參數(shù)抡锈,我們稱之為 實(shí)參疾忍。

形參只有在被調(diào)用時(shí)才分配內(nèi)存單元,在調(diào)用結(jié)束時(shí)床三,即刻釋放所分配的內(nèi)存單元一罩。因此形參只在函數(shù)內(nèi)部有效,形參的作用域只限于函數(shù)內(nèi)撇簿,函數(shù)調(diào)用結(jié)束返回主調(diào)函數(shù)后則不能再使用該形參變量聂渊。

實(shí)參可以是變量名或?qū)ο螅瑹o論實(shí)參是何種類型四瘫,在進(jìn)行函數(shù)調(diào)用時(shí)汉嗽,他們都必須有確定的值,以便把這些值賦值給形參找蜜。

在這里我們明確形參和實(shí)參的概念饼暑,以便于我們下面對(duì)參數(shù)的定義和賦值進(jìn)行更清晰、更深層次的理解洗做。


1弓叛、參數(shù)的傳遞

在 Python 中,函數(shù)參數(shù)(argument竭望,也叫作 parameter)的傳遞是通過自動(dòng)將對(duì)象(實(shí)參)賦值給參數(shù)名(形參)來實(shí)現(xiàn)的,也就是將實(shí)參所指向的對(duì)象(或者其本身就是一個(gè)對(duì)象)的引用賦值給形參裕菠。

函數(shù)參數(shù)的賦值咬清,在實(shí)際中只是 Python 賦值的另一個(gè)實(shí)例而已。因?yàn)橐檬且灾羔樀男问綄?shí)現(xiàn)的奴潘,所有的參數(shù)實(shí)際上都是通過指針進(jìn)行傳遞的旧烧。作為參數(shù)被傳遞的對(duì)象從來不會(huì)進(jìn)行拷貝。

函數(shù)參數(shù)的賦值和創(chuàng)建并不會(huì)影響調(diào)用者程序中的變量名(而不是變量名所指向的對(duì)象)的作用域和使用画髓。因?yàn)樵诤瘮?shù)運(yùn)行時(shí)掘剪,在函數(shù)頭部的參數(shù)名(形參)是一個(gè)新的、本地的變量名奈虾,這個(gè)變量名是在函數(shù)的本地作用域內(nèi)的夺谁。

如果賦值給函數(shù)參數(shù)的是可變對(duì)象,那么改變參數(shù)的值會(huì)對(duì)調(diào)用者有影響肉微。因?yàn)閷?duì)象是簡單的通過引用賦值給函數(shù)的參數(shù)匾鸥,函數(shù)能夠就地改變傳入的可變對(duì)象,因此其結(jié)果會(huì)影響調(diào)用者碉纳∥鸶海可變參數(shù)對(duì)于函數(shù)來說是可以做輸入和輸出的。

像整數(shù)和字符串這樣的不可變對(duì)象是不可以原處修改的劳曹,因此如果函數(shù)修改了傳入的不可變對(duì)象奴愉,實(shí)際的效果就是用新的對(duì)象重新賦值給了函數(shù)參數(shù)琅摩。


如何避免可變參數(shù)的修改

對(duì)可變參數(shù)原處修改的行為不是一個(gè)bug——它只是參數(shù)傳遞在Python中的工作方式。在Python中锭硼,默認(rèn)通過引用(也就是指針)進(jìn)行函數(shù)的參數(shù)傳遞房资,是因?yàn)檫@通常是我們想要的:這意味著不需要?jiǎng)?chuàng)建多個(gè)對(duì)象的拷貝(只用將對(duì)象指向新的變量名)就可以在我們的程序中傳遞很大的對(duì)象,并且能夠按照需要方便的更新這些對(duì)象账忘。

如果不想要函數(shù)內(nèi)部在原處的修改影響傳遞給它的對(duì)象志膀,那么,我們可以簡單地創(chuàng)建一個(gè)明確的可變對(duì)象的拷貝鳖擒。對(duì)于函數(shù)參數(shù)溉浙,我們總是能夠在調(diào)用時(shí)針對(duì)列表進(jìn)行拷貝。

如果不想改變傳入的對(duì)象蒋荚,無論函數(shù)是如何調(diào)用的戳稽,我們同樣可以在函數(shù)內(nèi)部進(jìn)行拷貝。

這兩種拷貝的機(jī)制都不會(huì)阻止函數(shù)改變對(duì)象:這樣做僅僅是防止了這些改變會(huì)影響調(diào)用者期升。為了真正意義上防止這些改變惊奇,我們總是能夠?qū)⒖勺儗?duì)象轉(zhuǎn)換為不可變對(duì)象來杜絕這種問題。

這種方法從某種意義上來說有些過于極端:因?yàn)檫@種方法強(qiáng)制函數(shù)寫成絕不改變傳入?yún)?shù)的樣子播赁,這種辦法強(qiáng)制對(duì)函數(shù)比原本應(yīng)該的進(jìn)行了更多的限制颂郎,所以通常意義下應(yīng)該避免出現(xiàn)∪菸或許將來你會(huì)發(fā)現(xiàn)對(duì)于一些調(diào)用來說改變參數(shù)是有用的一件事情乓序。使用這種技術(shù)會(huì)讓函數(shù)失去一種參數(shù)能夠調(diào)用任意列表特定方法的能力,包括不會(huì)在原處改變對(duì)象的那些方法都不再能夠使用坎背。

這里最需要記住的就是替劈,函數(shù)能夠升級(jí)為傳入可變對(duì)象(例如,列表和字典)
的形式得滤,這不會(huì)是一個(gè)問題陨献,并且有時(shí)候這對(duì)于有些用途很有用處。此外懂更,原處修改傳入的可變對(duì)象的函數(shù)眨业,可能是為此而設(shè)計(jì)并有意而為之——修改可能是一個(gè)定義良好的API的一部分,而我們不應(yīng)該通過產(chǎn)生副本來違反改API沮协。


2坛猪、函數(shù)定義時(shí)參數(shù)的類型

這里我們主要介紹的是 Python 的形參。

2.1 標(biāo)準(zhǔn)參數(shù)

就是我們最常用的參數(shù)皂股,在函數(shù)定義時(shí)墅茉,直接以變量名的形式定義。在函數(shù)調(diào)用時(shí),標(biāo)準(zhǔn)參數(shù)必須提供就斤,否則會(huì)報(bào)錯(cuò)悍募。


2.2 默認(rèn)參數(shù)

函數(shù)能夠?yàn)閰?shù)定義接收的默認(rèn)值,在函數(shù)定義中洋机, 默認(rèn)參數(shù)以賦值語句 name=value 的形式提供坠宴。在函數(shù)調(diào)用時(shí)如果沒有提供這個(gè)參數(shù), 它就使用這個(gè)默認(rèn)值绷旗。

python 中所有的標(biāo)準(zhǔn)參數(shù)必須出現(xiàn)在任何一個(gè)默認(rèn)參數(shù)之前喜鼓。如果一個(gè)參數(shù)具有默認(rèn)值,所有隨后的參數(shù)直到 “*” 號(hào)也必須具有默認(rèn)值 —— 這個(gè)限制在語法中沒有表達(dá)出來的衔肢。

在函數(shù)定義被執(zhí)行時(shí)時(shí)庄岖,Python 會(huì)從左到右的計(jì)算默認(rèn)參數(shù)值。這意味著當(dāng)函數(shù)被定義角骤,默認(rèn)參數(shù)已經(jīng)被創(chuàng)建并被賦值了隅忿,如果在以后的調(diào)用中沒有為默認(rèn)參數(shù)重新賦值(重新指向新的對(duì)象),那么它將一直使用函數(shù)定義時(shí)創(chuàng)建的值(對(duì)象)邦尊。理解這點(diǎn)對(duì)于默認(rèn)參數(shù)是可變對(duì)象時(shí)特別重要背桐,例如列表或字典:如果函數(shù)在調(diào)用時(shí)修改了該對(duì)象(例如,向列表添加一個(gè)元素)蝉揍,默認(rèn)值將受影響被修改链峭,這將會(huì)影響函數(shù)的下一次調(diào)用。這通常不是想要的又沾。你可以將默認(rèn)參數(shù)指定為不可變對(duì)象或者在函數(shù)中避免修改默認(rèn)參數(shù)弊仪。

默認(rèn)參數(shù)是在 def 語句運(yùn)行時(shí)評(píng)估并保存的,而不是在這個(gè)函數(shù)調(diào)用時(shí)捍掺。從內(nèi)部來講撼短,Python會(huì)將每一個(gè)默認(rèn)參數(shù)保存成一個(gè)對(duì)象再膳,附加在這個(gè)函數(shù)本身挺勿。

因?yàn)槟J(rèn)參數(shù)是在 def 時(shí)被評(píng)估的,如果必要的話喂柒,它能夠從整個(gè)作用域(函數(shù)定義的作用域)中保存值不瓶,但是因?yàn)槟J(rèn)參數(shù)在調(diào)用之間都保存了一個(gè)對(duì)象,必須對(duì)修改可變的默認(rèn)參數(shù)十分小心灾杰。

有些人把這種行為當(dāng)做一種特性蚊丐。因?yàn)榭勺冾愋偷哪J(rèn)參數(shù)在函數(shù)調(diào)用之間保存了他們的狀態(tài),從某種意義上講它們能夠充當(dāng)C語言中的靜態(tài)本地函數(shù)變量的角色艳吠。在一定程度上麦备,它們工作起來就像全局變量,但是它們的變量名對(duì)于函數(shù)來說是本地變量,而且不會(huì)與程序中的其他變量名發(fā)生沖突凛篙。

為什么使用默認(rèn)參數(shù)

默認(rèn)參數(shù)讓程序的健壯性上升到極高的級(jí)別黍匾,因?yàn)樗鼈冄a(bǔ)充了標(biāo)準(zhǔn)位置參數(shù)沒有提供的一些靈活性。當(dāng)少幾個(gè)需要操心的參數(shù)時(shí)候呛梆,生活不再那么復(fù)雜锐涯。利用默認(rèn)參數(shù)你可以為你的API 接口提供一個(gè)常用的默認(rèn)配置,這在一個(gè)程序員剛接觸到一個(gè) API 接口并沒有足夠的知識(shí)來給參數(shù)提供更對(duì)口的值時(shí)顯得尤為有幫助填物。

使用默認(rèn)參數(shù)的概念與在你的電腦上安裝軟件的過程類似纹腌。一個(gè)人會(huì)有多少次選擇默認(rèn)安裝而不是自定義安裝?我可以說可能幾乎都是默認(rèn)安裝滞磺。這既方便升薯,易于操作,又能節(jié)省時(shí)間雁刷。如果你是那些總是選擇自定義安裝的頑固分子覆劈,請記著你只是少數(shù)人之一

另外一個(gè)讓開發(fā)者受益的地方在于,使開發(fā)者更好地控制為顧客開發(fā)的軟件沛励。當(dāng)提供了默認(rèn)值的時(shí)候责语,他們可以精心選擇“最佳“的默認(rèn)值,所以用戶不需要馬上面對(duì)繁瑣的選項(xiàng)目派。隨著時(shí)間流逝坤候,當(dāng)用戶對(duì)系統(tǒng)或者 api 越來越熟悉的時(shí)候,他們最終能自行給出參數(shù)值企蹭,便不再需要使用“學(xué)步車“了白筹。


2.3 可變長參數(shù)(參數(shù)組)

在Python中可以通過定義參數(shù)組,從而讓函數(shù)可以接收任意多額外沒有匹配的基于位置或關(guān)鍵字的參數(shù)谅摄⊥胶樱基本上,你可以將所有參數(shù)放進(jìn)參數(shù)組中送漠,僅僅用這些裝有參數(shù)的容器作為參數(shù)來調(diào)用一個(gè)函數(shù)顽照,而不必顯式地將所有的參數(shù)都放在函數(shù)調(diào)用中。

(1)非關(guān)鍵字參數(shù)(元組)

當(dāng)函數(shù)被調(diào)用的時(shí)候闽寡,調(diào)用者傳遞的位置參數(shù)(實(shí)參)都將按照順序賦給了在函數(shù)聲明中相對(duì)應(yīng)參數(shù)(形參)代兵。剩下的非關(guān)鍵字參數(shù)按順序插入到一個(gè)元組中以便于訪問∫罚可變長的參數(shù)元組必須在位置參數(shù)和默認(rèn)參數(shù)之后植影。

在函數(shù)定義時(shí),使用帶 * 號(hào)的參數(shù)名(通常我們命名為 args)把收集到的不匹配的位置參數(shù)包含在一個(gè)元組中作為參數(shù)傳遞給函數(shù)涎永。


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

在函數(shù)調(diào)用時(shí)思币,當(dāng)有額外的關(guān)鍵字參數(shù)(實(shí)參)沒有與函數(shù)定義的參數(shù)(形參)相匹配時(shí)鹿响,這些關(guān)鍵字參數(shù)會(huì)被放入一個(gè)字典中,字典中鍵為參數(shù)名谷饿,值為相應(yīng)的參數(shù)值抢野。
在函數(shù)定義時(shí),使用帶 ** 號(hào)的參數(shù)名(通常我們命名為 kwargs)把接收的關(guān)鍵字參數(shù)包含在一個(gè)字典中作為參數(shù)傳遞給函數(shù)各墨。


2.4 Keyword-only 參數(shù)

Python3.0 把函數(shù)頭部的排序規(guī)則通用化了指孤,并允許我們指定 keyword-only 參數(shù)(形參)——即必須只按照關(guān)鍵字傳遞并且不能由一個(gè)位置參數(shù)(實(shí)參)來填充的參數(shù)(可以理解成必須按照關(guān)鍵字形式賦值的默認(rèn)參數(shù))。如果想要一個(gè)函數(shù)只能按照關(guān)鍵字形式來傳遞配置選項(xiàng)的話贬堵,這是很有用的恃轩。

從語法上將睁宰,keyword-only 參數(shù)編碼為命名的參數(shù)建蹄,出現(xiàn)在參數(shù)(形參)列表中的 *args 之后谬盐。所有這些參數(shù)都必須在調(diào)用中使用關(guān)鍵字語法來傳遞闷畸。

例如,如下代碼中触趴,a可能按照名稱或位置傳遞幻捏,b收集任何額外的位置參數(shù)前普,并且c必須只按照關(guān)鍵字傳遞宏所。

我們也可以在參數(shù)列表中使用一個(gè)字符酥艳,來表示一個(gè)函數(shù)不會(huì)接受一個(gè)變量長度的參數(shù)列表,而是仍然期待跟在后面的所有參數(shù)都作為關(guān)鍵字傳遞爬骤。

仍然可以對(duì) keyword-only 參數(shù)使用默認(rèn)值充石,即便它們出現(xiàn)在函數(shù)頭部的 * 后面。


排序規(guī)則

keyword-only 參數(shù)必須在一個(gè)單個(gè)星號(hào)后面指定霞玄,而不是兩個(gè)星號(hào)——命名的參數(shù)不能出現(xiàn)在 **kwargs 形式的后面骤铃,并且一個(gè) ** 不能獨(dú)自出現(xiàn)在參數(shù)列表中。

這意味著坷剧,在一個(gè)函數(shù)頭部惰爬,keyword-only 參數(shù)必須編寫在 **kwargs 參數(shù)之前,且在 *args 參數(shù)之后(當(dāng)二者都有的時(shí)候)惫企。無論何時(shí)撕瞧,一個(gè)參數(shù)名稱出現(xiàn)在 *args 之前,它可能是默認(rèn)位置參數(shù)雅任,而不會(huì)是 keyword-only 參數(shù)风范。

在函數(shù)調(diào)用中咨跌,類似的排序規(guī)則也是成立的:當(dāng)傳遞 keyword-only 參數(shù)的時(shí)候沪么,它們必須出現(xiàn)在一個(gè) **name(實(shí)參) 之前。keyword-only 參數(shù)(實(shí)參)可以放在在 *name(實(shí)參) 之前或者之后锌半,并且可能包含在 **args 中禽车。


為何使用 keyword-only 參數(shù)

簡而言之寇漫,它們使得很容易允許一個(gè)函數(shù)既接收任意多個(gè)要處理的位置參數(shù),也接收作為關(guān)鍵字傳遞的配置選項(xiàng)殉摔。


3州胳、參數(shù)的排列順序

在函數(shù)定義的頭部,參數(shù)必須以此順序出現(xiàn):標(biāo)準(zhǔn)參數(shù)(name)逸月,默認(rèn)參數(shù)(name=value)栓撞,如果有的話,后面是 *args 的形式碗硬,后面跟著任何 name=value 的keyword-only 參數(shù)瓤湘,后面跟著 **kwargs 參數(shù)。


《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)識(shí)符和關(guān)鍵字
Python基礎(chǔ)手冊 6 —— 操作符
Python基礎(chǔ)手冊 7 —— 內(nèi)建函數(shù)
Python基礎(chǔ)手冊 8 —— Python對(duì)象
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 —— 錯(cuò)誤 & 異常
Python基礎(chǔ)手冊27 —— 模塊
Python基礎(chǔ)手冊28 —— 模塊的高級(jí)概念
Python基礎(chǔ)手冊29 —— 包

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末恩尾,一起剝皮案震驚了整個(gè)濱河市弛说,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌翰意,老刑警劉巖木人,帶你破解...
    沈念sama閱讀 206,968評(píng)論 6 482
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異冀偶,居然都是意外死亡醒第,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,601評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門进鸠,熙熙樓的掌柜王于貴愁眉苦臉地迎上來淘讥,“玉大人,你說我怎么就攤上這事堤如∑蚜校” “怎么了?”我有些...
    開封第一講書人閱讀 153,220評(píng)論 0 344
  • 文/不壞的土叔 我叫張陵搀罢,是天一觀的道長蝗岖。 經(jīng)常有香客問我,道長榔至,這世上最難降的妖魔是什么抵赢? 我笑而不...
    開封第一講書人閱讀 55,416評(píng)論 1 279
  • 正文 為了忘掉前任,我火速辦了婚禮唧取,結(jié)果婚禮上铅鲤,老公的妹妹穿的比我還像新娘。我一直安慰自己枫弟,他們只是感情好邢享,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,425評(píng)論 5 374
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著淡诗,像睡著了一般骇塘。 火紅的嫁衣襯著肌膚如雪伊履。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,144評(píng)論 1 285
  • 那天款违,我揣著相機(jī)與錄音唐瀑,去河邊找鬼。 笑死插爹,一個(gè)胖子當(dāng)著我的面吹牛哄辣,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播赠尾,決...
    沈念sama閱讀 38,432評(píng)論 3 401
  • 文/蒼蘭香墨 我猛地睜開眼柔滔,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了萍虽?” 一聲冷哼從身側(cè)響起睛廊,我...
    開封第一講書人閱讀 37,088評(píng)論 0 261
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎杉编,沒想到半個(gè)月后超全,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,586評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡邓馒,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,028評(píng)論 2 325
  • 正文 我和宋清朗相戀三年嘶朱,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片光酣。...
    茶點(diǎn)故事閱讀 38,137評(píng)論 1 334
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡疏遏,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出救军,到底是詐尸還是另有隱情财异,我是刑警寧澤,帶...
    沈念sama閱讀 33,783評(píng)論 4 324
  • 正文 年R本政府宣布唱遭,位于F島的核電站戳寸,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏拷泽。R本人自食惡果不足惜疫鹊,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,343評(píng)論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望司致。 院中可真熱鬧拆吆,春花似錦、人聲如沸脂矫。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,333評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽羹唠。三九已至奕枢,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間佩微,已是汗流浹背缝彬。 一陣腳步聲響...
    開封第一講書人閱讀 31,559評(píng)論 1 262
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留哺眯,地道東北人谷浅。 一個(gè)月前我還...
    沈念sama閱讀 45,595評(píng)論 2 355
  • 正文 我出身青樓,卻偏偏與公主長得像奶卓,于是被迫代替她去往敵國和親一疯。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,901評(píng)論 2 345

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