7

考虑这些不同的行为::

>> def minus(a, b):
>>    return a - b

>> minus(**dict(b=2, a=1))
-1

>> int(**dict(base=2, x='100'))
4

>> import operator
>> operator.sub.__doc__
'sub(a, b) -- Same as a - b.'
>> operator.sub(**dict(b=2, a=1))
TypeError: sub() takes no keyword arguments

为什么operator.sub行为与 不同int(x, [base])

4

3 回答 3

7

这是一个实现细节。用于检索参数的Python C API 将位置参数和关键字参数分开。位置参数在内部甚至没有名称。

用于检索operator.add函数(以及类似的函数sub)参数的代码是这样的:

PyArg_UnpackTuple(a,#OP,2,2,&a1,&a2)

如您所见,它不包含任何参数名称。相关的整个代码operator.add是:

#define spam2(OP,AOP) static PyObject *OP(PyObject *s, PyObject *a) { \
  PyObject *a1, *a2; \
  if(! PyArg_UnpackTuple(a,#OP,2,2,&a1,&a2)) return NULL; \
  return AOP(a1,a2); }

spam2(op_add           , PyNumber_Add)
#define spam2(OP,ALTOP,DOC) {#OP, op_##OP, METH_VARARGS, PyDoc_STR(DOC)}, \
                           {#ALTOP, op_##OP, METH_VARARGS, PyDoc_STR(DOC)},
spam2(add,__add__, "add(a, b) -- Same as a + b.")

如您所见,唯一使用a和的地方b是在文档字符串中。方法定义也不使用METH_KEYWORDS方法接受关键字参数所必需的标志。

一般来说,你可以放心地假设一个你知道参数名称的基于 python 的函数将始终接受关键字参数(当然,有人可以通过*args解包做一些讨厌的事情,但创建一个参数看起来正常的函数文档),而 C 函数可能或可能不接受关键字参数。具有多个参数或可选参数的函数很有可能接受后面/可选参数的关键字参数。但是您几乎必须对其进行测试。

你可以在python-ideas 邮件列表中找到关于支持关键字参数的讨论。Guido van Rossum(The Benevolent Dictator For Life,又名 Python 的创造者)也发表了声明:

嗯。我认为对于许多(大多数?)1-arg 和选定的 2-arg 函数(很少是 3+-arg 函数),这会降低可读性,如 ord(char=x) 的示例所示。

我实际上希望看到一个语法特性来声明一个参数不能作为关键字参数给出(就像我们已经添加了语法来声明它必须是一个关键字一样)。

我认为添加关键字 args 的一个领域是完全错误的:内置类型或 ABC 的方法,它们是可覆盖的。例如,考虑 dict 上的 pop() 方法。由于参数名称当前未记录,如果有人将 dict 子类化并覆盖此方法,或者如果他们创建另一个尝试使用鸭子类型模拟 dict 的可变映射类,那么参数名称是什么并不重要——所有调用者 (期望 dict、dict 子类或类似 dict 的鸭子)将在调用中使用位置参数。但是,如果我们要记录 pop() 的参数名称,并且用户开始使用这些名称,那么大多数 dict 子类和鸭子会突然被破坏(除非他们碰巧选择了相同的名称)。

于 2012-06-05T08:48:35.717 回答
4

operator是一个 C 模块,它以不同的方式定义函数。除非模块初始化中的函数声明包含METH_KEYWORDS,否则该函数在任何情况下都不会接受关键字参数,并且您会收到问题中给出的错误。

于 2012-06-05T08:50:18.643 回答
3

minus(**dict(b=2, a=1))扩展到minus(b=2, a=1). 这是有效的,因为您的定义具有参数名称ab.

operator.sub(**dict(b=2, a=1))扩展到operator.sub(b=2, a=1). 这不起作用,因为 sub 不接受关键字参数。

于 2012-06-05T08:45:56.047 回答