這篇文章是偉兄給我的稿子,總結(jié)實(shí)用、到位。另外,歡迎訪問(wèn)并關(guān)注他的博客: https://jl-zhenlaixiaowei.blog.csdn.net/ 我曾經(jīng)和一個(gè)聰明的 Pythonista 結(jié)對(duì)編程,每次他輸入帶有可選或關(guān)鍵字參數(shù)的函數(shù)定義時(shí),他都會(huì)驚呼“argh!”和“kwargh!”。 要不然我們相處的很好,我猜想這就是學(xué)術(shù)界編程最終對(duì)人所帶來(lái)的影響吧。 現(xiàn)在args 和 kwargs 參數(shù)仍然是 Python 中非常有用的特性,而且理解它們的威力將使您成為更有效的開(kāi)發(fā)人員。 那么“args”和“kwargs”參數(shù)用來(lái)做什么呢?
它們?cè)试S一個(gè)函數(shù)接受可選參數(shù),因此你能夠在你的模塊和類(lèi)里創(chuàng)建彈性APIs。 In?[2 ]:?def ?foo (required,?*args,?**kwargs) : ???...:?????print(required) ???...:?????if ?args: ???...:?????????print(args) ???...:?????if ?kwargs: ???...:?????????print(kwargs) 上面的函數(shù)需要至少一個(gè)叫做“必須的”參數(shù),但是它也能接受額外的位置參數(shù)和關(guān)鍵字參數(shù)。 如果我們調(diào)用帶有附加參數(shù)的函數(shù),參數(shù)將會(huì)收集額外的位置參數(shù)作為一個(gè)元組,因?yàn)檫@個(gè)參數(shù)的名字有一個(gè)*(單星號(hào))前綴。 同樣地,kwargs將收集額外的關(guān)鍵字參數(shù)作為一個(gè)字典,因?yàn)檫@個(gè)參數(shù)名字有**(雙星號(hào))前綴。 如果沒(méi)有附加參數(shù)被傳遞給函數(shù)。args 和 kwargs 可以為空。 當(dāng)我們調(diào)用帶有參數(shù)的不同組合的函數(shù)時(shí),你會(huì)看到在args和kwargs內(nèi)部參數(shù)。 Python如何收集它們,根據(jù)它們是否為位置參數(shù)或者關(guān)鍵字參數(shù)。代碼如下: In?[3 ]:?foo() --------------------------------------------------------------------------- TypeError?????????????????????????????????Traceback?(most?recent?call?last)-3 -c19b6d9633cf>?in ? ---->?1 ?foo() TypeError:?foo()?missing?1 ?required?positional?argument:?'required' In?[4 ]:?foo('hello' ) hello In?[5 ]:?foo('hello' ,?1 ,?2 ,?3 ) hello (1 ,?2 ,?3 ) In?[6 ]:?foo('hello' ,?1 ,?2 ,?3 ,?key1='value' ,?key2=999 ) hello (1 ,?2 ,?3 ) {'key1' :?'value' ,?'key2' :?999 } 我要明確一下,調(diào)用args和kwargs參數(shù)是簡(jiǎn)單的命名慣例。 像之前的例子里,如果稱(chēng)他們*parms和**argv也可以。實(shí)際上語(yǔ)法分別是單星號(hào)(*)或者雙星號(hào)(**)。 然而,我還是推薦你還是堅(jiān)持可接受的命名慣例以避免混淆。(而且每隔一段時(shí)間還有機(jī)會(huì)喊“argh!”和“kwargh!”)。 ## 轉(zhuǎn)發(fā)可選或者關(guān)鍵字參數(shù)
有可能從一個(gè)函數(shù)到另一個(gè)函數(shù)傳遞可選或者關(guān)鍵字參數(shù)。 當(dāng)你調(diào)用要轉(zhuǎn)發(fā)參數(shù)的函數(shù)時(shí),你可以通過(guò)使用解包參數(shù)操作符*和**。在你傳遞之前這也給你一個(gè)機(jī)會(huì)修改參數(shù)。 In?[8 ]:?def ?foo (x,?*args,?**kwargs) : ???...:?????kwarg['name' ]?=?'Alice' ???...:?????new_args?=?args?+?('extra' ,?) ???...:?????bar(x,?*new_args,?**kwargs) 這種技術(shù)對(duì)于子類(lèi)化和編寫(xiě)包裝函數(shù)很有用。 例如,您可以使用它來(lái)擴(kuò)展父類(lèi)的行為,而不必在子類(lèi)中復(fù)制其構(gòu)造函數(shù)的完整簽名。 如果您使用的 API 可能會(huì)在您的控制之外發(fā)生變化,這會(huì)非常方便,示例代碼如下: In?[9 ]:?class ?Car : ???...:?????def ?__init__ (self,?color,?mileage) : ???...:?????????self.color?=?color ???...:?????????self.mileage?=?mileage ???...:? In?[10 ]:?class ?AlwaysBlueCar (Car) : ????...:?????def ?__init__ (self,?*args,?**kwargs) : ????...:?????????super().__init__(*args,?**kwargs) ????...:?????????self.color?=?'blue' In?[12 ]:?AlwaysBlueCar('green' ,?48392 ).color Out[12 ]:?'blue' AlwaysBlueCar 構(gòu)造函數(shù)只是將所有參數(shù)傳遞給它的父類(lèi),然后覆蓋一個(gè)內(nèi)部屬性。 這意味著如果父類(lèi)構(gòu)造函數(shù)發(fā)生更改,AlwaysBlueCar 很有可能仍會(huì)按預(yù)期運(yùn)行。 這里的缺點(diǎn)是 AlwaysBlueCar 構(gòu)造函數(shù)現(xiàn)在有一個(gè)相當(dāng)無(wú)用的簽名——如果不查找父類(lèi),我們不知道它需要什么參數(shù)。 通常,您不會(huì)將這種技術(shù)用于您自己的類(lèi)層次結(jié)構(gòu)。更有可能的情況是您想要修改或覆蓋某些您無(wú)法控制的外部類(lèi)中的行為。 但這總是危險(xiǎn)的領(lǐng)域,所以最好小心(否則你可能很快就會(huì)有另一個(gè)理由尖叫“argh!”)。 這種技術(shù)可能有用的另一種情況是編寫(xiě)包裝函數(shù),例如裝飾器。在那里,您通常還希望接受要傳遞給包裝函數(shù)的任意參數(shù)。 而且,如果我們可以在不必復(fù)制和粘貼原始函數(shù)簽名的情況下做到這一點(diǎn),那可能更易于維護(hù): import ?functoolsdef ?trace (f) : ????...:[email protected] (f) ????...:?????def ?decorated_function (*args,?**kwargs) : ????...:?????????print(f,?args,?kwargs) ????...:?????????result?=?f(*args,?**kwargs) ????...:?????????print(result) ????...:?????return ?decorated_function ???? In?[11 ]:?@trace ????...:?def ?greet (greeting,?name) : ????...:?????return ?'{},?{}!' .format(greeting,?name)In?[14 ]:?greet('Hello' ,?'Bob' )0x7fefa69db700 >?('Hello' ,?'Bob' )?{} Hello,?Bob!使用像這樣的技術(shù),有時(shí)很難在使代碼足夠明確的想法和遵守不要重復(fù)自己(DRY)原則的想法之間取得平衡。 這可能永遠(yuǎn)是一個(gè)艱難的選擇。如果你能從同事那里得到第二個(gè)意見(jiàn),我鼓勵(lì)你嘗試一下。 機(jī)器學(xué)習(xí)交流qq群955171419,加入微信群請(qǐng) 掃碼: