前言
本篇文章是对PEP 3102 – Keyword-Only Arguments这篇文章的翻译。PEP文章真是难看,一堆晦涩,加上能力不够,所以这篇翻译可能并不完美,需要阅读原英文文档的,可点击查看以上的链接。PDF版本可在此处下载 百度云提取码:wpnq。
这篇文章只是对python3新增加的keyword-only Argument的解释,关于python 函数位置参数,默认参数, 可变参数,可变关键字参数,可移步python 位置参数,默认参数, 可变参数,仅限关键字参数,可变关键字参数的详解及区别。
摘要
本篇PEP提出了一个对函数参数赋值到命名参数槽的方法的改变。特别是,支持声明仅限关键字参数:此种参数只能由关键字提供,绝对不会被位置参数自动填充。
原理阐述
当前Python的函数调用模式,允许通过位置或关键字指定参数。参数可以通过指定的名字显式填充或通过位置隐式填充。
通常情况下,一个函数会需要使用可变数量的参数,Python语言支持使用’varargs’语法(*name),指定将任何剩余的参数都以元组的方式传入这个可变参数。前提限制是,在填充varargs参数槽之前,所有的普通参数槽需要被填满。
这并不总是可取的,我们可以很容易的预想到:一个函数可能会需要一个可变数量的参数,但同时也需要一个或多个关键字参数形式的可选项。目前,实现这一点的唯一方法是同时定义一个可变参数vargas和一个’keywords’参数(kwargs),然后手动从字典中提取出可用的关键字。
使用说明
从语法上讲,本篇文章提议的更改相当简单,第一个改变是允许常规参数出现在可变参数之后(此时这个常规参数就是一个仅限关键字参数。强制性的,它只能通过关键字传参):
def sortwords(*wordlist,case_sensitive=False):
...
例如上面的函数可以接受任意数量的位置参数,同时也可以接受一个关键字参数‘case_sensitive’。因为这个关键字参数选项绝对不会被位置参数填充,而是必须显式的通过名字指定填充,所以称为仅限关键字参数。
仅限关键字参数不需要有默认值, 由于Python需要将所有的参数都绑定一个值,而且将值绑定到关键字参数的唯一方法是通过这个关键字,因此这种参数是‘需要关键字的参数’。所以这些参数必须通过调用方提供,且必须通过关键字提供值。
第二点语法上的更改是允许省略可变参数的参数名。这意味着对于一个有仅限关键字参数的函数来说,它不会再接受一个可变参数。如下例:
def compare(a, b, *, key=None):
...
此函数省略了可变参数名,它不在接受任何的可变参数,这种改变背后的原因:设想一个函数需要几个位置参数,同时这个函数也需要一个关键字参数,那么按照原来的方法我们可以这样定义函数:
def compare(a, b, key=None):
...
现在,假设你想要这个‘key’是一个仅限关键字参数,由上面第一点的语法,你可以通过先添加一个可变参数,之后再加入一个关键字参数,这样的话这个关键字参数就会是仅限关键字参数:
def compare(a, b ,*ignore, key=None):
...
不幸的是,这个可变参数‘ignore’会吸收任意多个由调用者提供的错误位置参数,。考虑到我们希望任何不需要的参数都能引发错误,可以这样做:
def compare(a, b, *ignore, key=None):
if ignore: # 如果ignore不为空
raise TypeError
方便点,我们可以简单的省略掉‘ignore’这个参数名, 意思是‘不允许任何超过这一点的位置参数’。因此,‘单星号语法可以表示为位置参数的结束符’。
函数的参数调用行为
当一个函数被调用时,输入的参数会以如下几种方式赋值给形参:
- 对于每个形参,都有一个参数槽用于存放将要赋给这个参数的值。
- 已经赋过值得参数槽被标记为‘filled’,没有赋值的参数槽仍然会被视为‘empty’
- 初始化时,所有的参数槽都被标记为空。
- 位置参数第一个被赋值,接下来是关键字参数。
- 对于每一个位置参数:
- 先尝试将值绑定到第一个空的参数槽,如果这个参数槽不是一个可变变量的参数槽,将它标记为’filled’
- 否则,如果下一个空槽是可变参数槽,就将所有剩下的非关键字参数值放到这个可变参数槽里。
- 对于每一个关键字参数:
- 如果存在与关键字命名相同的参数,就把这个实参的值赋给这个关键字参数槽。如果所有的参数槽都已经被填满了,会引发错误异常
- 否则,如果存在一个关键字 字典的关键字参数,这个参数会被添加到一个字典:关键字为此字典的键,如果这个键已经存在,会报错。
- 否则, 如果不存在keyword dictionary, 而且没有匹配到参数名,报错。
- 最后:
- 如果可变变量的参数槽仍然没有填充,则赋一个空的元组作为它的值。
- 对每一个剩余的空参数槽:如果这个参数槽有默认值,就把这个参数槽用默认值填充。如果没有默认值,报错。