預設參數
在程式設計中,一個函數的預設參數是指不必須指定值的參數。在大多數程式語言中,函數可以接受一個或多個參數。通常對於每個參數都需要指定它們的值(例如C語言[1])。一些較新的程式語言(例如C++)允許程式設計師設定預設參數並指定預設值,當呼叫該函數並未指定值時,該預設參數將為預設值。
C++中的預設參數
考慮如下函數聲明:
int my_func(int a, int b, int c=12);
該函數有三個參數,最後一個參數的預設值為12。程式設計師可以用以下兩種方式呼叫該函數:
result = my_func(1, 2, 3);
result = my_func(1, 2);
第一種呼叫方式中,c的值就像一般函數一樣被設為3。在第二種呼叫方式中,c被省略,而被直接賦值為預設值12。
在函數體中,無法知道參數值是在呼叫時指定的還是直接使用的預設值。
這種定義方法在希望不論是否指定參數時都可以呼叫函數的情況下尤其有用,考慮下面的例子:
void printToScreen(istream &input = cin)
{
// this outputs any input to the screen
cout << input;
}
函數呼叫:
printToScreen();
這個函數呼叫會預設將鍵盤輸入列印到螢幕輸出。通過這種用法列印到螢幕,比起下面的用法是更為合適的:
printToScreen(cin);
另一方面,任何一種輸入流都可以作為參數傳入該函數。此時該函數會將指定輸入流的內容輸出到螢幕,例如:
printToScreen(fileName);
fileName是通過檔案流ifstream打開的可讀檔案。
多載方法
在Java等一些其他語言中,不支援預設參數。但可以用過函數多載(方法多載)的方式來支援不同數量的參數。通過方法多載,讓參數少的方法直接呼叫參數多的方法並指定預設值,就可以達到同樣的效果。例如:
int MyFunc(int a, int b) { return MyFunc(a, b, 12); }
int MyFunc(int a, int b, int c) { /* main implementation here */ }
評價
對於每一次使用預設參數值的函數呼叫,預設的函數值都必須要傳遞一次,相比於函數多載,這造成了代碼臃腫。
如果預設參數不是一個值而是一個表達式,何時計算該表達式則是新的問題。究竟是整個程式執行期間計算一次(語法解釋時、編譯時或執行時)還是每一次呼叫都計算一次。
Python只在模組載入時計算一次預設參數列達式的值。如果我們需要每次呼叫都計算預設值,則可以將預設值設置為監視哨,例如設為None
。隨後在函數第一行,檢查監視哨,如果監視哨是預設值,則計算所需表達式的值。
例如我們可以通過以下方法使函數在每一次使用預設值時都重新取得當前的時間:
import datetime
def f(a, b=None):
b = b or datetime.datetime.now()
作用域和生存周期
一般而言,預設參數就像一個傳入的函數參數或一個在函數開頭定義的局部變數一樣,擁有和局部變數一致的作用域和生存周期。作為一個自動變數會在函數終止時被釋放。
在另外一些情況下,預設參數會被靜態分配。就像靜態變數,如果預設參數的值發生了改變,則在所有函數呼叫中都會發生相應改變。
這種情況在Python中存在。如果預設參數是可變類型(例如列表),則該預設參數就會被靜態分配。為保證預設參數為局部變數,可以使用監視哨之方法實現,就例如:
def f(a, b=None):
b = b or []