Python 函數(shù)(3)


本篇總結(jié)函數(shù)參數(shù)匹配(argument match)的問題

背景

對于函數(shù)的使用者而言,關(guān)注點(diǎn)有兩個(gè):
1、函數(shù)所提供的接口,即,輸入的問題;
2、函數(shù)的輸出;

輸入--->函數(shù)--->輸出

對于函數(shù)本身而言,關(guān)注點(diǎn)有三個(gè):
1、輸入,包括輸入的格式、輸入的內(nèi)容、校驗(yàn)等;
2、如何處理輸入;
3、提供什么樣的輸出;

輸入--->處理--->輸出

可見,使用者不關(guān)注函數(shù)的處理過程,只關(guān)注輸入和輸出,而函數(shù)則不關(guān)注使用者是誰,只關(guān)注輸入。
那么,可以發(fā)現(xiàn),函數(shù)提供輸入實(shí)際是提供接口,所以函數(shù)輸入設(shè)計(jì)的實(shí)質(zhì)是接口設(shè)計(jì)。
而接口設(shè)計(jì)又可以分為兩部分:形式設(shè)計(jì)和內(nèi)容設(shè)計(jì)。其中,形式設(shè)計(jì)回答什么形式的輸入的問題,內(nèi)容設(shè)計(jì)則回答輸入是什么的問題。

本篇要討論的函數(shù)參數(shù)匹配問題屬于形式設(shè)計(jì)。


好了,形而上的講了一堆東西,下面開始正題。

1. 參數(shù)匹配

首先來個(gè)C++的例子:
假設(shè),有函數(shù)如下:

int  record( string name, unsigned int age, string email)
{
     // 處理過程
      return 0;
}

該函數(shù)所提供的接口為 string name, unsigned int age, string email
那么如下例,使用函數(shù):

record("Joe", 20, "Joe@email.com");    // 正確形式
record(20, "Joe", "Joe@email.com");    // 錯(cuò)誤形式
record("Joe@email.com", 20, "Joe");    // 語法正確,但是內(nèi)容錯(cuò)誤

當(dāng)使用 record(20, "Joe", "Joe@email.com"); 會出現(xiàn)錯(cuò)誤。如果使用IDE那么,這里會提示錯(cuò)誤信息;如果不是使用IDE,那么在編譯時(shí),也會有相應(yīng)的錯(cuò)誤信息提示。錯(cuò)誤的原因是數(shù)據(jù)類型不匹配

也就是說,在C++中,參數(shù)的匹配時(shí)是按照順序來進(jìn)行的。即:
string name = "Jone",unsigned int age = 20,string email = "Joe@email.com",
對于靜態(tài)強(qiáng)類型語言,當(dāng)出現(xiàn)string name = 20的匹配時(shí),就會出現(xiàn)數(shù)據(jù)類型不匹配的錯(cuò)誤(不考慮模板的情況)。

基本上,常見的編程語言,函數(shù)的默認(rèn)參數(shù)匹配方式都采用順序匹配方式。Python也不例外。但是Python也提供了其他的參數(shù)匹配方式:
位置匹配(順序匹配<默認(rèn)采用這種方式>)
關(guān)鍵字匹配
默認(rèn)值
可變參數(shù)

2. 位置匹配

Python的位置匹配實(shí)例如下:

# 函數(shù)定義
>>> def record(name, age, email):
...     record_name = name
...     record_age  = age
...     record_email= email
... 
# 函數(shù)使用
>>> record('Joe', 20, 'Joe@email.com')    #
>>> record(20, 'Joe', 'Joe@email.com')      #形式正確,內(nèi)容錯(cuò)誤

對于動(dòng)態(tài)語言,上例中第二種調(diào)用,并沒有類型不匹配的問題。只是內(nèi)容錯(cuò)誤而已。由此也引出了關(guān)鍵字匹配

3. 關(guān)鍵字匹配

在上述例子中第二種調(diào)用方式之所以會出現(xiàn)問題,原因在于:
由于采用位置匹配,所以為了保證輸入內(nèi)容正確,調(diào)用時(shí),必須知曉參數(shù)的位置(或者順序)。
采用關(guān)鍵字匹配就不存在這種問題。
還是先看例子:

# 函數(shù)定義
>>> def record(name, age, email):
...     record_name = name
...     record_age  = age
...     record_email= email
... 
# 函數(shù)使用
>>> record(name = 'Joe', age = 20, email = 'Joe@email.com')    #
>>> record(age = 20, name = 'Joe', email = 'Joe@email.com')    #

可以看到,關(guān)鍵字匹配位置匹配的最大區(qū)別在于:關(guān)鍵字匹配,在調(diào)用函數(shù)時(shí),加入了實(shí)參與形參的對應(yīng)信息。
這樣做的好處在于,調(diào)用者只需要知道調(diào)用時(shí)需要輸入的參數(shù)名稱即可,不用理會其順序。另外,從語義的角度來看,這種方式在表達(dá)上也更加清晰,因?yàn)檎{(diào)用時(shí),十分清楚的寫明了:
age = 20name = 'Joe',email = 'Joe@email.com'
程序的可讀性非常高。

4. 默認(rèn)值

對于函數(shù)的調(diào)用者而言,調(diào)用時(shí)所需的參數(shù)越少,意味著使用起來也越簡單。但是,有時(shí)候,為了功能的靈活,同時(shí)需要保留足夠的備選參數(shù)。這些備選參數(shù)需要有以下特點(diǎn):

  1. 如果調(diào)用時(shí)不設(shè)定,則為默認(rèn)值;
  2. 如果調(diào)用時(shí)設(shè)定,則使用設(shè)定值;

這樣,使得函數(shù)的易用性與靈活性同時(shí)得到保證。默認(rèn)值參數(shù)就實(shí)現(xiàn)了這樣的功能。
舉例:

>>> def record(name, age, email="xxx@email.com"):
...     print name, age, email
... 
>>> record('Joe', 20, 'Joe@gmail.com')
Joe 20 Joe@gmail.com
>>> record('Joe', 20)                 
Joe 20 xxx@email.com

5. 可變參數(shù)

為什么需要可變參數(shù)

假設(shè)現(xiàn)在需要這樣一個(gè)函數(shù):輸入兩個(gè)字符串,輸出兩個(gè)字符串拼起來的字符串。

>>> def str_add(str1, str2):
...     return str1 + str2
... 
>>> str_add("I am ", "Joe ")
'I am Joe '

輸入兩個(gè)字符串的例子很容易實(shí)現(xiàn),那么問題來了... ....
如果要求輸入3個(gè)字符串呢?4個(gè)?5個(gè)?... ...總不能一個(gè)函數(shù)一個(gè)函數(shù)的寫下去吧。

總結(jié)一下,很容易發(fā)現(xiàn)不論輸入是幾個(gè)字符串,所作的操作都是將所輸入的字符串拼起來。這個(gè)時(shí)候,可變參數(shù)就派上用場了。
先看例子:

>>> def str_add(*str):                        # 定義可變參數(shù)
...     print reduce(lambda x,y:x+y,str)      # 將str中的所有變量相加
... 
>>> str_add("I am ", "Joe")
I am Joe
>>> str_add("I am ", "Joe, ","my email is ","Joe@gmail.com")
I am Joe, my email is Joe@gmail.com
>>> 

上例中,采用可變參數(shù)的函數(shù)str_add可以支持不定個(gè)數(shù)的字符串輸入,輸出這些字符串拼接之后的字符串。

由此可見:
可變參數(shù)的意義在于,能夠使函數(shù)處理參數(shù)不定,操作具有共性的需求。而所謂的可變參數(shù)是指,參數(shù)的個(gè)數(shù)是可以變化的。

定義可變參數(shù)
弄清楚為什么要使用可變參數(shù)以后,我們來說一下上面的例子中是如何定義可變參數(shù)的。
首先,我們使用Tuple可以實(shí)現(xiàn)一個(gè)功能與可變參數(shù)相同的函數(shù)。

>>> def str_add(str):
...     print reduce(lambda x,y:x+y, str)
... 
>>> words = ("I am ", "Joe")     #創(chuàng)建一個(gè)Tuple對象
>>> str_add(words)
I am Joe

對比這個(gè)例子和上面的例子可以發(fā)現(xiàn),在本例中,需要提前構(gòu)造一個(gè)Tuple對象,然后再將這個(gè)對象傳入函數(shù)。
實(shí)際上,上一節(jié)的例子中str_add函數(shù)內(nèi)部 *str接收到也是Tuple對象。

在Python中如果List對象或者Tuple對象使用*來修飾作為實(shí)參傳入函數(shù)時(shí),會將其變成可變參數(shù)列表傳入函數(shù)。
比如下面的例子:

>>> def str_add(*str):
...      print reduce(lambda x,y:x+y,str)
... 
>>> words = ("I am ", "Joe")
>>> str_add(*words)              #在Tuple對象words前加*作為實(shí)參傳入函數(shù)
I am Joe

可變參數(shù)在形式上有兩種:

  1. 對于List對象或者Tuple對象,在變量前加*;
  2. 對于Dict對象,在變量前加**;

在Python中稱加*的變量為Non-keyword Variable Arguments,稱加**的變量為keyword Variable Arguments。

下面給出一個(gè)可變參數(shù)使用的完整例子

>>> def foo(name, age, *args, **kwargs):
...     print name, age, args, kwargs
... 
>>> info = ["Joe@gmail.com","male"]
>>> addition = {"location":"Beijing","prof":"software"}
>>> foo("Joe",20, *info, **addition)        #List對象info前加*,Dict對象addition前加**
Joe 20 ('Joe@gmail.com', 'male') {'prof': 'software', 'location': 'Beijing'}
>>> foo("Joe",20, "Joe@gmail.com","male",location="Beijing",prof="software")    
Joe 20 ('Joe@gmail.com', 'male') {'prof': 'software', 'location': 'Beijing'}

總結(jié)一下:
Python的函數(shù)參數(shù)匹配提供了多種方式,非常靈活,使用的時(shí)候即可以單獨(dú)使用某一種方式,也可以混合使用多種方式。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • http://python.jobbole.com/85231/ 關(guān)于專業(yè)技能寫完項(xiàng)目接著寫寫一名3年工作經(jīng)驗(yàn)的J...
    燕京博士閱讀 7,803評論 1 118
  • 第5章 引用類型(返回首頁) 本章內(nèi)容 使用對象 創(chuàng)建并操作數(shù)組 理解基本的JavaScript類型 使用基本類型...
    大學(xué)一百閱讀 3,677評論 0 4
  • 本章將會介紹 控制流For-In 循環(huán)While 循環(huán)If 條件語句Switch 語句控制轉(zhuǎn)移語句 continu...
    寒橋閱讀 809評論 0 0
  • Spring Cloud為開發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見模式的工具(例如配置管理,服務(wù)發(fā)現(xiàn),斷路器,智...
    卡卡羅2017閱讀 136,554評論 19 139
  • A這幾天不在, 一個(gè)人害怕, 這幾天就在B的房間里睡覺, 雖然會失眠, 雖然會被睡著的他偶爾揩油, 但是這一切對于...
    8dbfd81d8788閱讀 181評論 0 0

友情鏈接更多精彩內(nèi)容