Consider these different behaviour::
>> 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
Why does operator.sub
behave differently from int(x, [base])
?
It is an implementation detail. The Python C API to retrieve arguments separates between positional and keyword arguments. Positional arguments do not even have a name internally.
The code used to retrieve the arguments of the operator.add
functions (and similar ones like sub
) is this:
PyArg_UnpackTuple(a,#OP,2,2,&a1,&a2)
As you can see, it does not contain any argument name. The whole code related to operator.add
is:
#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.")
As you can see, the only place where a
and b
are used is in the docstring. The method definition also does not use the METH_KEYWORDS
flag which would be necessary for the method to accept keyword arguments.
Generally spoken, you can safely assume that a python-based function where you know an argument name will always accept keyword arguments (of course someone could do nasty stuff with *args
unpacking but creating a function doc where the arguments look normal) while C functions may or may not accept keyword arguments. Chances are good that functions with more than a few arguments or optional arguments accept keyword arguments for the later/optional ones. But you pretty much have to test it.
You can find a discussion about supporting keyword arguments everywhere on the python-ideas mailinglist. There is also a statement from Guido van Rossum (the Benevolent Dictator For Life aka the creator of Python) on it:
Hm. I think for many (most?) 1-arg and selected 2-arg functions (and rarely 3+-arg functions) this would reduce readability, as the example of ord(char=x) showed.
I would actually like to see a syntactic feature to state that an argument cannot be given as a keyword argument (just as we already added syntax to state that it must be a keyword).
One area where I think adding keyword args is outright wrong: Methods of built-in types or ABCs and that are overridable. E.g. consider the pop() method on dict. Since the argument name is currently undocumented, if someone subclasses dict and overrides this method, or if they create another mutable mapping class that tries to emulate dict using duck typing, it doesn't matter what the argument name is -- all the callers (expecting a dict, a dict subclass, or a dict-like duck) will be using positional arguments in the call. But if we were to document the argument names for pop(), and users started to use these, then most dict sublcasses and ducks would suddenly be broken (except if by luck they happened to pick the same name).
operator
is a C module, which defines functions differently. Unless the function declaration in the module initialization includes METH_KEYWORDS
, the function will not accept keyword arguments under any conditions and you get the error given in the question.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With