PEP-3102 建议了 Keyword-Only Arguments,并且在 Python3 中实现。本文试着在 Python 函数参数的基本理解 基础上,更深入的理解 Keyword-Only Arguments. 普通用法 在 位置参数 和 关键字参数 中我们看到,普通情形下,一个参数既可以通过位置捕获,也可以直接用名称捕获。 举个例子: >>> def f(a): ... print(a) ... >>> f(1) # 通过位置隐式地捕获 1 >>> f(a=1) # 通过 Keyword Arguments 形式显式地提供 1 >>> 定义 Keyword-Only Arguments,意思是这种参数只能通过形如 a=1 的写法给出,不再接受通过位置隐式地捕获。 具体的做法是用一个单独的星号 * 参数,表示其后面的参数全部必须以 Keyword Arguments 形式提供。 举个例子: >>> def f(*, a): # 声明 a 必须以 Keyword Arguments 形式提供 ... print(a) ... >>> f(1) # 尝试通过位置隐式提供,被抛出异常 Traceback (most recent call last): File "", line 1, in TypeError: f() takes 0 positional arguments but 1 was given >>> >>> f(a=1) # 通过 Keyword Arguments 形式显式地提供 1 >>> 初衷 在 这一节 中提到,Python 2.x 的时代,(*a, b) 这么定义函数是不合法的: 因为前面的 *a 会收集它后面的所有位置参数,如果存在一个 b, 一定会被收集到 *a 中。 但是有这么一种场景需要(*a, b) 的写法:*a 是一系列主要参数,而 b 是一些辅助的、可选的、不那么重要的参数,可以不作为 位置参数 提供,而是作为 关键字参数 提供。 既然 *a 只收集位置参数,那么将 b 作为 关键字参数 提供,并不会引发歧义。 这就是 仅限关键字参数 Keyword-Only Arguments 的初衷。 例子 内置函数 max() 就是个很好的例子,定义如下: max(arg1, arg2, *args[, key]) 前面所有的 args 是参与比较的数值或者其他变量,而最后一个可选参数 key 以关键字参数的形式提供函数对象。 举个例子: >>> max(1,2,3,4,5) 5 >>> max(1,2,3,4,5,key=lambda x:-x) 1 >>> 这个 key 参数是可选的、补充的。与此类似的还有 print() >>> print('a', 'b', 'c', 'd', sep='|') a|b|c|d >>> 实现 为了实现这一需求,需要对 Python 2.x 的语法做出改变。 第1步改变 首先要做的是:允许 f(*a, b) 这种参数形式。下面的例子展示了两个版本不同的反应。 Python 2.x # Python 2.x >>> def f(*a, b): File "", line 1 def f(*a, b): ^ SyntaxError: invalid syntax >>> Python 3.x # Python 3.x >>> def f(*a, b): ... pass ... >>> 可见在 Python2中不接受这种形式,而Python 3.x则允许了,允许的前提是,后面的参数必须以关键字形式提供:也就是类似 f(b=1) 这种形式,而不能仅仅提供一个数值,即 f(1) 这种形式是不接受的。 为什么能支持关键字参数,这里有一个隐含的逻辑是:f(*a, b) 参数列表中,*a 负责收集所有的 位置参数,而且是贪婪的行为,即尽可能多的收集位置参数,直到碰到第一个例外为止。如果这里碰到一个关键字参数,那么自然 *a 停止收集,就为后面的关键字参数的存在提供了可能性。 仅限关键字参数(Keyword-Only Arguments)是对调用者(也就是函数 arguments)的要求,而非对设计者(也就是 parameter)的要求。 具体说来,在声明函数的时候,没有必要为了显示关键字参数,而给出默认值;但是在使用函数的时候,必须以关键字参数的形式提供数值,而非位置参数。 第2步改变 第2步改变是在 第1步 基础上,进一步对语法的放宽。 现在已经允许这种写法:f(*a, b),但是有一种场景:关键字参数 b 前面没有 *a 这种 可变长度位置参数 的需求,而是一些固定的位置参数。除此之外,仅仅想规定后面的 b 为仅限关键字参数(Keyword-Only Arguments)。 下面举个例子: def add(left, right, key): ... 这个二元加法函数明确的只接受两个操作数,而第三个参数 key 则希望规定为仅限关键字参数(Keyword-Only Arguments)。例如: # 不接受 add('a', 'bc', len) # 仅接受 add('a', 'bc', key=len) key 前面没有了类似 *a 这样的可变长度参数引导。需要设计一种新语法标记后方的参数为仅限关键字参数(Keyword-Only Arguments)。仁慈的终生独裁者(BDFL):Guido van Rossum 决定使用单个星号作为位置参数的结束标志。 上面的函数声明应该写作: def add(left, right, *, key): ...