Python基礎-函數參數
寫在前面
如非特別說明,下文均基于Python3
摘要
本文詳細介紹了函數的各種形參類型,包括位置參數,默認參數值,關鍵字參數,任意參數列表,強制關鍵字參數;也介紹了調用函數時傳遞實參的各種方式,包括位置實參,關鍵字實參以及使用*和**來解包序列和字典。
1. 概述
函數在一定程度上是為了重用而創建的。如果有一段非常優秀的代碼段,實現了網絡資源下載的功能,如果沒有函數,將會在每次需要實現網絡資源下載的地方復制該段代碼。懶惰即美德,將這段代碼抽象為函數,在需要使用的地方調用即可。
函數的使用有以下好處:
- 增加代碼的可讀性。如在需要下載網絡資源的地方調用函數:
download()
,可以通過名字讀懂程序的目的; - 增加代碼可重用性。相比復制大段代碼,調用函數的可操作性無疑更強;
- 增加可維護性。如果需要更改下載網絡資源的實現,沒有使用函數的情況下,不得不在每個實用下載功能的地方修改,使用了函數,只需要修改函數即可;
- 減少犯錯誤的可能性。在復制代碼的過程中,無疑會因為各種原因出現一些差錯,而函數不會。
函數定義非常簡單:
def func([formal_parameter1, ... formal_parameter1]):
statement
以上函數定義的作用是創建函數對象,并且在當前作用域創建名字func
,指向函數對象,在可及該作用域范圍內,可以使用名字func
調用函數。定義函數時候參數列表中的名字是函數形參,調用函數用的參數是實參。
Python
函數的參數十分強大,但相應也為這種強大付出了相對復雜的代價。
函數定義時,函數的形參可以有以下幾種類型:
- 位置參數 positional parameters,最常用的形參形式,位置比名字重要;
- 默認參數值 default argument values,param_name = argu_value形式,為形參提供默認值,必須放置在位置參數之后;
- 任意參數列表 arbitrary argument lists,*args形式,args以元組的形式接收未匹配的位置實參;
- 關鍵字形參字典 keyword arguments, **kwargs形式,kwargs以字典的形式接收未匹配的關鍵字實參,關鍵字參數需在任意參數列表之后;
- 強制關鍵字參數 keyword-only arguments,在任意參數列表之后(或者在單獨的*之后),調用是只能使用關鍵字實參。
函數調用時,實參可以由以下方式傳遞:
- 位置實參按照位置從左到右匹配,位置比名字重要;
- 關鍵字實參,通過明確形參的名字為其指定實參值。調用時關鍵字實參必須在位置實參之后,且形參列表中要有與之匹配的關鍵字形參;
- 解包列表/字典,使用*(sequence)從序列中解包位置實參,使用**(dict)的方式從字典中解包關鍵字實參。
當這些不同的形參組合在一起時,構成的函數參數列表將會相當復雜,始終牢記實參形參匹配是位置參數優先。而且,任意參數列表與關鍵字參數組合的形參列表,可以匹配任意方式的函數調用。
2. 位置參數
位置形參是最常見的形參類型,其中,位置比名字重要,因為在實參匹配是是按照位置來的:
# positional argument, name is not important, but order matters
def positional_argument(name, age):
print('name->type:%s, value:%s' % (type(name), name))
print('age->type:%s, value:%s' % (type(age), age))
調用時,如果改變實參位置,意義完全不同:
positional_argument('Richard', 20)
positional_argument(20, 'Richard')
位置形參和位置實參(統稱位置參數)是最重要的參數類型,在參數匹配中它的優先級是最高的。
3. 參數默認值
有其他高級語言(如java)經驗的人知道,有重載函數這一說法,兩個函數的名字相同,其參數列表不同,功能不同。調用者通過指定不同的實參,調用不同形參的重載函數。
但是在Python中沒有重載函數的說法,因為默認參數值得存在,是的調用者在調用同一個函數的時候可以指定不同參數。雖然不支持重載,但是Python以默認參數值的方式實現了重載函數的功能。
指定了默認參數值的形參不能位于位置參數之前,因為實參匹配是位置優先的,這時在前面的指定了默認值的參數會被位置實參覆蓋,導致后面的位置形參無法匹配到實參值而調用失敗:
# default argument values, non-default argument cann't follow default argument
def default_argument_value(name, age = 20, id = '0001'):
print('name->type:%s, value:%s' % (type(name), name))
print('age->type:%s, value:%s' % (type(age), age))
print('id->type:%s, value:%s' % (type(id), id))
# 調用時,可以有多種實參形式
# 指定唯一的強制參數
default_argument_value('Richard')
# 指定其中一個默認參數
default_argument_value('Richard', 22)
# 指定全部參數
default_argument_value('Richard', 22, '002')
4. 任意參數列表
Python的函數相較于其他高級語言強大的地方在于,可以收集多余的未匹配到形參的實參。使用如下格式的形參:*args
,收集到尚未匹配到形參的實際參數。
接收的額外位置實參以元組的形式存儲,且任意參數列表需要在位置參數之后:
# Arbitrary Argument Lists
# It receives a tuple containing the positional arguments beyond the formal parameter list. (*name must occur before **name.)
# be last in the list of formal parameters, because they scoop up all remaining input arguments that are passed to the function
def arbitrary_arguments_list(name, age, *args):
print('name->type:%s, value:%s' % (type(name), name))
print('age->type:%s, value:%s' % (type(age), age))
print('args->type:%s, value:%s' % (type(args), args))
# 實參1, 2, 3沒有位置形參匹配,被任意參數列表收集
arbitrary_arguments_list('Richard', 20, 1, 2, 3)
output:
name->type:<class 'str'>, value:Richard
age->type:<class 'int'>, value:20
args->type:<class 'tuple'>, value:(1, 2, 3)
5. 關鍵字參數
在調用函數時,通過位置參數方式調用,每個參數到底匹配哪個形參是不容易發現的,之后查看函數定義才能知道。可以通過指定形參對應的實參值的方式調用,這樣實參形參的匹配更加明了。
還是以位置形參為例:
# positional argument, name is not important, but order matters
def positional_argument(name, age):
print('name->type:%s, value:%s' % (type(name), name))
print('age->type:%s, value:%s' % (type(age), age))
在調用時可以通過關鍵字方式:
# keyword arguments.
# In a function call, keyword arguments must follow positional arguments.
# All the keyword arguments passed must match one of the arguments accepted by the function, and their order is not important.
positional_argument(age = 20, name = 'Richard')
關鍵字實參必須在位置實參之后,并且可以在形參列表中匹配到形參名字,否則調用失敗:
# 形參中沒有名為id的參數,所以調用失敗
positional_argument(age = 20, name = 'Richard', id = '003')
收集多余關鍵字實參
任意參數列表能夠接收沒有匹配到位置形參的實參,而關鍵字形參字典能夠接受為匹配到關鍵字參數的實參。通過如**kwargs
的方式,收集尚未匹配的關鍵字實參,關鍵字參數字典也要在位置參數之后:
# keyword arguments dict **kwargs.
# It receives a dictionary containing all keyword arguments except for those corresponding to a formal parameter.
def keyword_argument_dict(name, age, **kwargs):
print('name->type:%s, value:%s' % (type(name), name))
print('age->type:%s, value:%s' % (type(age), age))
print('kwargs->type:%s, value:%s' % (type(kwargs), kwargs))
keyword_argument_dict(name = 'Richard', age = 20, id = '0001', type = 'it')
output:
name->type:<class 'str'>, value:Richard
age->type:<class 'int'>, value:20
kwargs->type:<class 'dict'>, value:{'id': '0001', 'type': 'it'}
另外,關鍵字形參字典需要在任意參數列表之后。
6. 強制關鍵字參數
任意出現在*arg
或者*
之后的形參都是命名關鍵字參數,意味著它們只能作為關鍵字實參匹配,而非位置實參。
# Keyword only argument.
# Any formal parameters which occur after the *args parameter are ‘keyword-only’ arguments,
# meaning that they can only be used as keywords rather than positional arguments.
def keyword_only_argument(name, *, age, id):
print('name->type:%s, value:%s' % (type(name), name))
print('age->type:%s, value:%s' % (type(age), age))
print('id->type:%s, value:%s' % (type(id), id))
keyword_only_argument('Richard', age = 20, id = '001')
output:
name->type:<class 'str'>, value:Richard
age->type:<class 'int'>, value:20
id->type:<class 'str'>, value:001
7. 序列和字典實參的解包
在函數調用時,使用*sequence
將序列解包為位置實參;
使用**dict
將字典解包為關鍵字實參。
def mix_param(name, *args, **kwargs):
print('name->type:%s, value:%s' % (type(name), name))
print('args->type:%s, value:%s' % (type(args), args))
print('kwargs->type:%s, value:%s' % (type(kwargs), kwargs))
mix_param('Richard', *(1, 2, 3), **{'age':20, 'id':'001'})
output:
name->type:<class 'str'>, value:Richard
args->type:<class 'tuple'>, value:(1, 2, 3)
kwargs->type:<class 'dict'>, value:{'age': 20, 'id': '001'}
注意到*args
解包為位置參數,而**kwargs
解包為關鍵字參數,涵蓋了Python中所有可能出現的實參類型。因此,可以使用這兩個組合調用任意形參實行的函數:
def foo(x, y, z, m = 0, n = 0):
print(x, y, z, m, n)
def call_foo(*args, **kwargs):
print('Call foo!')
foo(*args, **kwargs)
注意到call_foo
函數中,args
是一個元組,kwargs
是一個字典,所以可以解包他們組合調用任意形參形式的函數。這一種方式在調用父類構造函數時非常有用!