1.位置参数
调用函数时,传入的值按照参数的位置顺序依次赋给参数。
2.默认参数
默认参数很有用,但是使用不当可能掉坑里,默认参数有个最大的坑,如下:
def add_end(L=[]):
L.append('End')
return L
使用默认参数时,结果是正确的:
>>>add_end()
['End']
但是,再次调用时,结果就不对了:
>>>add_end()
['End', 'End']
>>>add_end()
['End', 'End', 'End']
原因:Python函数在定义时,默认参数L
的值就被计算出来了,即[]
,因为L
也是一个变量,它指向对象[]
,每次调用该函数,如果改变了[]
的内容,则下次调用时,默认参数的内容就变了,不再是[]
了。
解决方案:使用None这个不变对象来实现
def add_end(L=None):
if L is None:
L = []
L.append('END')
return L
无论怎么调用都不会出错:
>>>add_end()
['End']
>>>add_end()
['End']
为什么要设计str、None这样的不变对象呢。不变对象一旦创建,对象内部的数据就不能修改,这样就减少了因为修改数据导致的错误,此外,由于对象不变,多任务环境下同时读取对象不需要加锁,同时读一点问题都没有。编写程序时,如果能设计一个不变对象,尽量设计成不变对象。
3.可变参数
即传入的参数个数是可变的。以一个数学题为例,给定一组数字a,b,c...
,计算a2+b2+c2+...
要定义出这个函数,我们必须确定输入的参数,由于参数个数不确定,可以想到把a,b,c作为一个list或tuple传进来,如下:
def calc(numbers):
sum = 0
for n in numbers:
sum = sum + n*n
return sum
>>>calc([1,2,3])
14
>>>calc([])
0
利用可变参数,可变为如下:
def calc(*numbers):
sum = 0
for n in numbers:
sum = sum + n*n
return sum
>>>calc(1,2,3)
14
>>>calc()
0
如有一已知的list或tuple,调用时前面加*号:
list1 = [1,2,3]
>>>calc(*list1)
14
4.关键字参数
关键字参数允许传入0个或任意个含参数名的参数,这些关键字参数在函数内部自动组装为一个dict。
函数person
除了必选参数name
和age
外,还接受关键字参数kw
,在调用函数时,可以只传入必选参数,也可以传入任意个的关键字参数:
test.py:
def person(name, age, **kw):
print('name:', name, 'age:', age, 'other:', kw)
extra = {'city': 'Beijing', 'job': 'Engineer'}
person('Tom', 40)
person('Jack', 25, city='Beijing', job = 'Engineer')
person('Jack', 25, **extra)
iTerm终端:
>>>python3 ~/python_test/test.py
name: Tom age: 40 other: {}
name: Jack age: 25 other: {'city': 'Beijing', 'job': 'Engineer'}
name: Jack age: 25 other: {'city': 'Beijing', 'job': 'Engineer'}
注意:kw获得的dict是extra的一份拷贝,对kw的改动不会影响到函数外地extra。
关键字参数可以扩展函数功能,比如用在用户注册信息时,有些信息是必填项,有些信息是选填项。
5.命名关键字参数
对于关键字参数,函数调用者可以传入任意不受限制的关键字参数,至于到底传入了哪些,需要在函数内部进行检查:
test.py:
def person(name, age, **kw):
if 'city' in kw:
print("there is a 'city' key")
if 'job' in kw:
print("there is a 'job' key")
print('name:', name, 'age:', age, 'other:', kw)
extra = {'city': 'Beijing', 'job': 'Engineer'}
person('Tom', 40)
person('Jack', 25, **extra)
iTerm终端:
>>>python3 ~/python_test/test.py
name: Tom age: 40 other: {}
there is a 'city' key
there is a 'job' key
name: Jack age: 25 other: {'city': 'Beijing', 'job': 'Engineer'}
但是调用者仍然可以传入不受限制的关键字参数,如果要限制关键字参数的名字,就可以使用命名关键字参数,例如,只接收city
和job
作为关键字参数,则定义如下:
def person(name, age, *, city, job):
print(name, age, city, job)
和关键字参数**kw
不同,命名关键字参数需要一个特殊的分隔符*
,*
后面的参数视为命名关键字参数,其调用如下:
>>>person('Jack', 25, city='Beijing', job = 'Engineer')
Jack 25 Beijing Engineer
如果函数定义中已经有一个可变参数,后面跟着的命名关键字参数就不再需要一个特殊的分隔符*
了:
# ~/python_test/test.py
def person(name, age, \*args, city, job):
print(name, age, args, city, job)
person('Jack', 24, 'Beijing', 'Engineer')
命名关键字参数必须传入参数名,这和位置参数不同,如果没有传入参数名,系统将报错:
>>>python3 ~/python_test/test.py
Traceback (most recent call last):
File "/Users/lijing/python_test/test.py", line 4, in
person('Jack', 25, 'Beijing', 'Engineer')
TypeError: person() missing 2 required keyword-only arguments: 'city' and 'job'
由于调用时缺少参数名city
和job
,python解释器把四个参数都视为了位置参数,但是person函数只接受name
和age
两个位置参数,因此报错。此外命名关键字参数还可以使用默认缺省值:
def person(name, age, *, city = 'Beijing', job):
print(name, age, city, job)
调用时可以不传入city
参数,使用默认值'Beijing'
注意:使用命名关键字时,要特别注意*
的使用,没有可变参数时必须加一个*
作为特殊分隔符,如果缺少*
,python解释器将无法识别位置参数和命名关键字参数。
6.参数组合
在python中定义函数,可以用必选参数、默认参数、可变参数、关键字参数和命名关键字参数,这5种参数都可以组合使用,但注意参数定义顺序必须是必选参数、默认参数、可变参数、命名关键字参数、关键字参数:
函数在调用时,python解释器自动按照参数位置和参数名把对应的参数传进去,而且,通过一个tuple和dict,也可以调用该函数:
# ~/python_test/test.py
def f1(a, b, c=0, *args, **kw):
print(a, b, c, args, kw)
def f2(a, b, c=0, *, d, **kw):
print(a, b, c, d, kw)
args = {1, 2, 3}
kw = {'d':99, 'x':'#'}
f1(1,2)
f1(1,2,c=3)
f1(1,2,3,'a','b')
f1(1,2,3,'a','b',x=99)
f2(1,2,d=99,ext=None)
f1(*args, **kw)
f2(*args, **kw)
#iTerm2终端:
>>>python3 ~/python_test/test.py
1 2 0 () {}
1 2 3 () {}
1 2 3 ('a', 'b') {}
1 2 3 ('a', 'b') {'x': 99}
1 2 0 99 {'ext': None}
1 2 3 () {'d': 99, 'x': '#'}
1 2 3 99 {'x': '#'}