Python 请解释为什么传入关键字参数时这两个内置函数的行为不同

Python 请解释为什么传入关键字参数时这两个内置函数的行为不同,python,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(**

考虑这些不同的行为:

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

减号(**dict(b=2,a=1))
扩展为
减号(b=2,a=1)
。这是因为您的定义有参数名
a
b


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

这是一个实现细节。位置参数和关键字参数之间的分隔。位置参数内部甚至没有名称

用于检索
运算符的参数的代码。add
函数(以及类似的
sub
)如下所示:

PyArg_解包元组(a,#OP,2,2,&a1,&a2)
如您所见,它不包含任何参数名称。与操作员添加相关的整个代码是:

#定义spam2(OP,AOP)静态PyObject*OP(PyObject*s,PyObject*a){\
PyObject*a1、*a2\
如果(!PyArg_unpactuple(a,#OP,2,2,&a1,&a2))返回NULL\
返回AOP(a1,a2);}
spam2(op_add,PyNumber_add)
#定义spam2(OP,ALTOP,DOC){#OP,OP###OP,METH_VARARGS,PyDoc_STR(DOC)}\
{#ALTOP,op###op,METH#u VARARGS,PyDoc#u STR(DOC)},
spam2(添加,添加,添加,添加(a,b)--与a+b.相同)
如您所见,使用
a
b
的唯一位置是docstring。方法定义也不使用
METH_KEYWORDS
标志,这是方法接受关键字参数所必需的

一般说来,您可以安全地假设一个基于python的函数,如果您知道一个参数名,它将始终接受关键字参数(当然,有人可能会用
*args
解包,但创建一个参数看起来正常的函数文档)而C函数可能接受也可能不接受关键字参数。具有多个参数或可选参数的函数很可能接受后面的/可选参数的关键字参数。但你几乎必须测试它

您可以在网站上找到关于支持关键字参数的讨论。Guido van Rossum(又名Python的创建者)对此也有一句话:

嗯。我认为对于许多(大多数?)1-arg和选定的2-arg函数(以及 很少有3+-arg函数)这会降低可读性,例如 显示了ord(字符=x)的数量

实际上,我希望看到一个语法特性来说明 参数不能作为关键字参数提供(正如我们已经做的那样) 添加了语法以说明它必须是关键字)

我认为添加关键字args是完全错误的:方法 内置类型或ABC的,可重写的。例如考虑 dict.上的pop()方法,因为参数名称当前为 未记录,如果有人将dict子类化并重写此方法,或 如果他们创建另一个尝试模拟的可变映射类 dict使用duck类型,参数名是什么并不重要-- 所有调用方(应为dict、dict子类或类似dict duck)将在调用中使用位置参数。但如果我们是 记录pop()的参数名称,用户开始使用 这些,然后大多数dict子机箱和鸭子会突然被打破 (除非他们碰巧取了同一个名字)


操作符
是一个C模块,它。除非模块初始化中的函数声明包括
METH\u关键字
,该函数在任何情况下都不会接受关键字参数,您会得到问题中给出的错误。

我很确定他在问为什么它不接受关键字参数,而您没有回答。您是说operator.sub的参数名为
?@BonAmi:没错
operator.sub
的参数为nameless@Eric所以
int
不是在C中实现的吗?我该怎么查?@BonAmi:不知道你能不能查到<我的第一个猜测是code>inspect.getargspec
,但C函数中存在错误。非常确定
int
是用C实现的,但是带有显式命名的参数。如果人们也能回答为什么这个函数选择不使用关键字参数,那就太好了,我也想知道这一点:关于风格选择,我应该指出,接受关键字参数会增加函数调用的开销,如果用户实际上通过关键字将参数传递给内置函数,则开销会显著增加。Python 3.5中的微基准表示
int(“0”,2)
vs.
int(“0”,base=2)
在我的机器上运行后者需要约2.5倍的时间;解析参数花费的时间比实际工作花费的时间要多。更糟糕的是函数只使用一个参数,这有一个避免参数包装的快速路径,但移动到关键字意味着参数必须包装在
元组和/或
dict
中。