Python 的函数参数详解
Python
的参数很复杂,有命名、非命名、*args
、**kwargs
等等,很容易混淆。记下来防止以后忘记。
参数类型
其实文档里是有这个的,在 inspect.Parameter
里。从定义的角度看:
Name | Meaning |
---|---|
POSITIONAL_ONLY | 只能根据位置来赋值(Python 里无法定义这样的参数,但是很多内置函数是这样的) |
POSITIONAL_OR_KEYWORD | 可以根据位置来赋值,也可以通过 keyword 赋值(这是 Python 里函数的标准行为方式) |
VAR_POSITIONAL |
一个 tuple 表示多个参数,即 *args
|
KEYWORD_ONLY |
只能通过 keyword 赋值(指的是定义在 *args
之后的参数——*args 会取走所有 unnamed
参数,故不能根据位置获得参数值)
|
VAR_KEYWORD |
一个 dict 表示多个 named 参数,即 **kwargs
|
注:用 inspect.signature
可以检视函数各个参数的详细信息。
从调用的角度看,函数分为:
- named 表示命名参数
- unnamed 表示非命名参数
但是实际中是有带*
和**
的东西出现的,接着看。
调用者传入参数
对于参数顺序,Python 本身有以下限制:
**
参数必须位于一般参数和*
参数之后,即只能放最后(但是可以不止一个)- named 参数必须在 unnamed 参数之后
这里有个 bug (or feature):带 *
参数可以放在 named
之后。这种情况下仍然是先 positional 地匹配——等价于把带 *
参数移到前面。
根据 Python 语言规范,unamed 参数必须写在 named 参数之前。
传参过程是这样的:
- 带
*
的参数被展开(unpack)成若干个 unnamed 参数 - 带
**
的参数展开成若干个命名 named 参数 - 其他的,直接传,不用动
注意,以上过程都是 in place 的,即相对左右其他参数的位置保持不变。
*
和 **
只是一个语法糖,它做的事情很简单,就是碰到的时候立马展开,变成一排
unnamed 或 named 参数。怎么样,简单吧!
被调用者接收参数
Python 对形参表有以下限制:
*args
和**kwargs
只能出现一次**kwargs
参数必须放在最后
谨慎使用 KEYWORD_ONLY
的参数!它的定义方式非常
implicit。
接受函数的过程是这样的:
- 一般参数,即
POSITIONAL_OR_KEYWORD
参数,直接拿到当前位置的(positional)unamed 值,无论该参数是否有 default VAR_POSITIONAL
参数拿走所有剩下的 unamed 参数KEYWORD_ONLY
从所有的 named 参数中匹配,如果匹配不到又没有默认值,则报错VAR_KEYWORD
拿到所有剩下的 named 参数,包括没被POSITIONAL_OR_KEYWORD
拿走的和没被KEYWORD_ONLY
拿走的
每个参数不能被赋值两次(即使值一样),没有 default 的函数必须被赋值。否则,报 TypeError。
总结
可以把参数看成两部分:一个 unnamed 参数组成的序列(tuple)和一个 named 参数组成的 dict。
这也解释了为什么
1 | def forward(*args, **kwargs): |
能实现转发。
tuple 按位置匹配,dict 按 keyword 匹配,不能漏,不能重复。