深入解析Python函数参数类型及其常见错误与解决方案
1 使用各种参数的陷阱

Python 功能强大、用途广泛的原因之一就是函数参数类型的多样化。方便的同时,也给用户带来了更多的约束规则。如果不理解这些规则,调用函数时可能会出现以下语法异常:
(1) 语法错误:位置参数跟随关键字参数
(2) TypeError: f() 缺少 1 个必需的仅关键字参数:'b'
(3) 语法错误:关键字参数重复
(4) 类型错误:f() 缺少 1 个必需的位置参数:'b'
(5) TypeError: f() 得到了意外的关键字参数 'a'
(6) 类型错误:f() 接受 0 个位置参数,但给出了 1 个
2 总结主要参数使用规则
位置参数
位置参数的定义:调用函数时,根据函数定义的参数位(形参)来传递参数。它是最常见的参数类型。
def f(a):
return a
f(1) # 位置参数
位置参数不能缺失:
def f(a,b):
pass
f(1) # TypeError: f() missing 1 required positional argument: 'b'
规则一:位置参数必须一一对应,缺一不可。
关键字参数
调用函数时,通过“key-value”方式传递函数参数的值,而不是根据位置传递函数参数的值。
def f(a):
print(f'a:{a}')
在这个调用中,a是关键字参数:
f(a=1)
但下面的调用是不行的:
f(a=1,20.) # SyntaxError: positional argument follows keyword argument
规则 2:关键字参数必须位于位置参数的右侧
下面的调用也是不行的:
f(1,width=20.,width=30.) #SyntaxError: keyword argument repeated
规则3:不能向同一个形参重复传递值
默认参数
定义函数时,可以为形参提供默认值。对于有默认值的形式参数,如果调用函数时为参数传递了值,则使用传递的值,否则使用默认值。下面的b是默认参数:
def f(a,b=1):
print(f'a:{a}, b:{b}')
规则4:无论是函数定义还是调用,默认参数的定义都应该在位置参数的右侧。
定义时只赋值一次;默认参数通常应定义为不可变类型
可变位置参数
定义如下的参数a是一个可变位置参数:
def f(*a):
print(a)
调用方法:
f(1) #打印结果为元组:(1,)
f(1,2,3) #打印结果:(1, 2, 3)
但是,它不能这样调用:
f(a=1) # TypeError: f() got an unexpected keyword argument 'a'
可变关键字参数
下面的a是可变关键字参数:
def f(**a):
print(a)
调用方法:
f(a=1) #打印结果为字典:{'a': 1}
f(a=1,b=2,width=3) #打印结果:{'a': 1, 'b': 2, 'width': 3}
但是,它不能这样调用:
f(1) TypeError: f() takes 0 positional arguments but 1 was given
3 五种参数的使用示例
今天我举一个python5参数使用的例子:位置参数、关键字参数、默认参数、可变位置或关键字参数。
def f(a,*b,c=10,**d):
print(f'a:{a},b:{b},c:{c},d:{d}')
默认参数c不能跟在可变关键字参数d后面。
呼叫 f:
In [10]: f(1,2,5,width=10,height=20)
a:1,b:(2, 5),c:10,d:{'width': 10, 'height': 20}
可变位置参数b解析为实参后的元组(2,5); c 获取默认值 10; d 被解析为字典。
再次调用 f:
In [11]: f(a=1,c=12)
a:1,b:(),c:12,d:{}
当传入a=1时,a为关键字参数,b和d不传入值,c传入12而不是默认值。
注意观察参数a,它可以是f(1)或f(a=1)。它的可读性比第一个更好。推荐使用f(a=1)。如果要强制f(a=1),需要在前面加一个星号:
def f(*,a,*b):
print(f'a:{a},b:{b}')
这时候调用f(1)就会报错:TypeError: f() Takes 0 Positional Argument but 1 was Give
只有f(a=1)才可以。
说明前面的*起作用了,就变成只能传入关键字参数了,那么如何检查这个参数的类型呢?在 python 的检查模块的帮助下:
In [22]: for name,val in signature(f).parameters.items():
...: print(name,val.kind)
...:
a KEYWORD_ONLY
b VAR_KEYWORD
可以看到参数a的类型为KEYWORD_ONLY,只是一个关键字参数。
然而,如果 f 定义为:
def f(a,*b):
print(f'a:{a},b:{b}')
查看参数类型:
In [24]: for name,val in signature(f).parameters.items():
...: print(name,val.kind)
...:
a POSITIONAL_OR_KEYWORD
b VAR_POSITIONAL
可以看到参数a既可以是位置参数也可以是关键字参数。