首先给出一个最基本的函数:
def say_hello(name):
print('Hello', name)
if __name__ == '__main__':
say_hello('James')
运行结果
默认参数
在我们的例子中,如果我们不知道他人的名称,如何向他人 say_hello 呢。这个就可以用默认参数,例如一般情况是和男士打招呼(程序媛太少了呀),代码如下
def say_hello(name='Man'):
print('Hello', name)
if __name__ == '__main__':
say_hello()
say_hello('James')
运行结果:
可以看到,如果我们没有像函数传递参数,函数参数就是取默认值 Man,如果传递了参数,传递的参数就会覆盖掉默认值。
默认参数一般设置为很少修改的参数变量。
可变参数:*args
如果我们相求一个 list 的平方和,一种实现是:
def add_to_sum(args):
total = 0
for i in args:
total += (i * i)
print(total)
if __name__ == '__main__':
l = [1, 2, 3]
add_to_sum(l)
add_to_sum([1, 2, 3])
我们发现每次调用函数都要构建一个 list 或者 tuple 之类的可迭代对象。我们可以使用可变参数来换一种方式。
def add_to_sum(*args):
total = 0
for i in args:
total += (i * i)
print(total)
if __name__ == '__main__':
add_to_sum(1, 2, 3)
add_to_sum(2, 4, 6)
这样就可以直接输入任意可变长度的参数(即,可变参数)。可变参数允许你传入0个或任意个参数,这些可变参数在函数调用时自动组装为一个tuple。
当然如果想要传一个 list 变量也可以的,只需要在调用函数的时候在函数变量之前加一个*即可。
def add_to_sum(*args):
total = 0
for i in args:
total += (i * i)
print(total)
if __name__ == '__main__':
l = [1, 3, 5]
s = (5, 10, 15, 20)
add_to_sum(*l)
add_to_sum(*s)
关键词参数: **kw
关键字参数允许你传入0个或任意个含参数名的参数,这些关键字参数在函数内部自动组装为一个dict。
def person(**kw):
print('other:', kw)
if __name__ == '__main__':
person()
person(name='James')
关键词参数和可变参数的不同点在于:关键词参数传入的是一个 dict,即含有参数名和值;可变参数传入的是一个 tuple。相同点都是任意长度(包括 0 个)。
同样的,如果想直接将一个 dict 类型的变量传入函数作为关键词参数,那么和可变长参数类似,变量名之前加 **。
def person(**kw):
print('other:', kw)
if __name__ == '__main__':
extra = {'School': 'UESTC', 'Hobby': 'Basketball'}
person(**extra)
命名关键词参数
如果要限制关键字参数的名字,就可以用命名关键字参数,例如,只接收 school 作为关键字参数。命名关键字参数需要一个特殊分隔符 *,* 后面的参数被视为命名关键字参数
def person(*, school):
print(school)
if __name__ == '__main__':
extra = {'school': 'UESTC', 'Hobby': 'Basketball'}
person(**extra)
person()
这个时候运行会报错,因为我们多输入一个名为 Hobby 的参数。
如果我们不填命名关键词参数呢:
def person(*, school):
print(school)
if __name__ == '__main__':
extra = {'school': 'UESTC'}
person()
也会报错,也就是说命名关键词参数是必填的!
def person(*, school):
print(school)
if __name__ == '__main__':
extra = {'school': 'UESTC'}
person(**extra)
如果函数定义中已经有了一个可变参数,后面跟着的命名关键字参数就不再需要一个特殊分隔符 * 了:
def person(name, age, *args, city, job):
print(name, age, args, city, job)
这个函数里面 args 是可变参数,city 和 job 就是命名关键词参数。
命名关键字参数可以有缺省值,和默认值参数一样的使用方法。
使用命名关键字参数时,要特别注意,如果没有可变参数,就必须加一个*作为特殊分隔符。如果缺少*,Python解释器将无法识别位置参数和命名关键字参数。
def person(name, age, city, job):
pass
例如这里的 city 和 job 都是一般形式的位置参数。
参数顺序:
在Python中定义函数,可以用必选参数、默认参数、可变参数、关键字参数和命名关键字参数,这5种参数都可以组合使用。但是请注意,参数定义的顺序必须是:必选参数、默认参数、可变参数、命名关键字参数和关键字参数。
廖雪峰老师博客中一个问题:为什么默认参数要在必选参数之后,不能先默认参数后必选参数。原因其实很简单:这样子如果传一个参数,如何确定是根据位置来的必选参数呢还是默认参数呢。
典例分析:
def f1(a, b, c=0, *args, **kw):
print('a =', a, 'b =', b, 'c =', c, 'args =', args, 'kw =', kw)
def f2(a, b, c=0, *, d, **kw):
print('a =', a, 'b =', b, 'c =', c, 'd =', d, 'kw =', kw)
1. f1(1, 2)
显然 1 和 2 是位置必选参数,可变参数和关键词参数都为空
2. f1(1, 2, c=3)
显然 1 和 2 是位置必选参数,c=3 显示的替换了默认值
3. f1(1, 2, 3, ‘a’, ‘b’)
显然 1, 2 和 3 是位置必选参数。‘a’ 和 ‘b’ 都不是键值对形式,显然就是可变长参数。
4. f1(1, 2, 3, ‘a’, ‘b’, x=99)
显然 1, 2 和 3 是位置必选参数。‘a’ 和 ‘b’ 都不是键值对形式,显然就是可变长参数。x=99 是键值对形式,显然就是关键词参数。
5. f2(1, 2, d=99, ext=None)
显然 1 和 2 是位置必选参数,c 没有赋值,显然就是取默认值。* 之后就是命名关键词参数,ext = None 是键值对形式,就是关键词参数。
6. 通过 tuple 或 list 传入
def f1(a, b, c=0, *args, **kw):
print('a =', a, 'b =', b, 'c =', c, 'args =', args, 'kw =', kw)
def f2(a, b, c=0, *, d, **kw):
print('a =', a, 'b =', b, 'c =', c, 'd =', d, 'kw =', kw)
args = (1, 2, 3, 4)
kw = {'d': 99, 'x': '#'}
f1(*args, **kw)
这个将 args 拆开之后先要填充必选项(包括默认参数),然后才是变长参数。类似的先将 kw 拆开之后填充命名关键词参数,再是关键词参数。
所以结果如下:
def f1(a, b, c=0, *args, **kw):
print('a =', a, 'b =', b, 'c =', c, 'args =', args, 'kw =', kw)
def f2(a, b, c=0, *, d, **kw):
print('a =', a, 'b =', b, 'c =', c, 'd =', d, 'kw =', kw)
args = (1, 2, 3)
kw = {'d': 88, 'x': '#'}
f2(*args, **kw)
同样的分析:args 拆开之后去赋值必选的参数(包括默认参数)。类似的先将 kw 拆开之后填充命名关键词参数,再是关键词参数。
结果如下:
一个问题就是如果 kw 变量里面没有 d 这个键值对,情况如何啊:
def f1(a, b, c=0, *args, **kw):
print('a =', a, 'b =', b, 'c =', c, 'args =', args, 'kw =', kw)
def f2(a, b, c=0, *, d, **kw):
print('a =', a, 'b =', b, 'c =', c, 'd =', d, 'kw =', kw)
args = (1, 2, 3)
kw = {'x': '#'}
f2(*args, **kw)
显然,代码会报错,理由如上, 命名关键词参数为必填的。
再一个问题,如果传入的 tuple 仅够填充必填字段呢:
def f1(a, b, c=0, *args, **kw):
print('a =', a, 'b =', b, 'c =', c, 'args =', args, 'kw =', kw)
def f2(a, b, c=0, *, d, **kw):
print('a =', a, 'b =', b, 'c =', c, 'd =', d, 'kw =', kw)
args = (1, 2, 3)
kw = {'x': '#'}
f1(*args, **kw)
显然是可以的,可变长参数实际传入为空。
再一个问题,如果传入的 tuple 不够填充必填字段呢:
def f1(a, b, c=0, *args, **kw):
print('a =', a, 'b =', b, 'c =', c, 'args =', args, 'kw =', kw)
def f2(a, b, c=0, *, d, **kw):
print('a =', a, 'b =', b, 'c =', c, 'd =', d, 'kw =', kw)
args = (1,)
kw = {'x': '#'}
f1(*args, **kw)
显然也是会报错的:
感悟
* 和 ** 变量对应 tuple 和 dict,但是不一定完全对应可变长参数和关键词参数。在 * 中拆开之后先要填充必填的位置参数和默认参数,填充完成之后剩余的才是可变长参数,当然如果填充不够必填参数会报错。类似的 ** 拆开之后要先赋值命名关键词参数,赋值之后剩余的才是关键词参数,当然如果没有命名关键词对应的 key,也是报错。