请解释为什么这两个内置函数在传递关键字参数时表现不同

时间:2012-06-05 08:43:35

标签: python

考虑这些不同的行为::

>> 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])不同?

3 个答案:

答案 0 :(得分:7)

这是一个实现细节。 Python C API to retrieve arguments分隔位置和关键字参数。位置参数内部甚至没有名称。

用于检索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.")

正如您所看到的,唯一使用ab的地方是docstring。方法定义也不使用METH_KEYWORDS标志,该标志是接受关键字参数所必需的。

一般来说,你可以放心地假设你知道一个参数名称的基于python的函数将总是接受关键字参数(当然有人可以通过*args解包来做一些讨厌的事情但是创建函数doc,其中参数看起来正常)而C函数可能接受或不接受关键字参数。有多个参数或可选参数的函数接受后来/可选参数的关键字参数,这是很好的。但你几乎要测试它。

您可以在python-ideas mailinglist上找到关于支持关键字参数的讨论。还有来自 Guido van Rossum Benevolent Dictator For Life又称Python的创造者)的声明:

  

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

     

我实际上希望看到一个语法功能来声明一个   参数不能作为关键字参数给出(正如我们已经提到的那样)   添加语法以声明必须是关键字)。

     

我认为添加关键字args的一个方面是完全错误的:方法   内置类型或ABC并且可以覆盖。例如。考虑一下   dict上的pop()方法。由于参数名称是当前的   没有文档,如果某个子类dict并覆盖此方法,或者   如果他们创建另一个尝试模拟的可变映射类   使用鸭子打字的dict,参数名称是什么并不重要 -   所有调用者(期待dict,dict子类或dict-like)   duck)将在调用中使用位置参数。但如果我们是   记录pop()的参数名称,并开始使用用户   这些,然后大多数dict sublcasses和鸭子会突然被打破   (幸运的是,他们碰巧选择了相同的名字)。

答案 1 :(得分:4)

operator是一个C模块,defines functions differently。除非模块初始化中的函数声明包含METH_KEYWORDS,否则该函数在任何条件下都不会接受关键字参数,并且您会得到问题中给出的错误。

答案 2 :(得分: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不接受关键字参数。