Python 的 Keyword-Only Arguments 理解

发布日期 2022-01-05
最后修改 2022-01-07
预计阅读时间 3 分钟
阅读量 67

PEP-3102 建议了 Keyword-Only Arguments,并且在 Python3 中实现。本文试着在 Python 函数参数的基本理解 基础上,更深入的理解 Keyword-Only Arguments.

普通用法

位置参数关键字参数 中我们看到,普通情形下,一个参数既可以通过位置捕获,也可以直接用名称捕获。

举个例子:

>>> def f(a):
...     print(a)
...
>>> f(1)
1
>>> f(a=1)
1
>>>

定义

Keyword-Only Arguments,意思是这种参数只能通过形如 a=1 的写法给出,不再接受通过位置隐式的捕获。

举个例子:

>>> def f(*, a):
...     print(a)
...
>>> f(1)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: f() takes 0 positional arguments but 1 was given
>>> f(a=1)
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 "<stdin>", 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):
    ...

参考资料

  1. https://www.python.org/dev/peps/pep-3102/

若无特别说明,本站文章均为原创,并采用 署名协议 CC-BY-NC 授权。
欢迎转载,惟请保留原文链接,且不得用于商业用途。