I came across this - in my view - strange behaviour:
"a b c".split(maxsplit=1)
TypeError: split() takes no keyword arguments
Why does str.split() not take keyword arguments, even though it would make sense? I found this behavior both in Python2 and Python3.
See this bug and its superseder.
str.split() is a native function in CPython, and as such exhibits the behavior described here:
CPython implementation detail: An implementation may provide built-in
functions whose positional parameters do not have names, even if they
are ‘named’ for the purpose of documentation, and which therefore
cannot be supplied by keyword. In CPython, this is the case for
functions implemented in C that use PyArg_ParseTuple() to parse their
arguments.
str.split is a builtin method implemented in C. Unfortunately some builtin functions/methods do not accept keyword arguments. See this bug report.
Related
I came across this - in my view - strange behaviour:
"a b c".split(maxsplit=1)
TypeError: split() takes no keyword arguments
Why does str.split() not take keyword arguments, even though it would make sense? I found this behavior both in Python2 and Python3.
See this bug and its superseder.
str.split() is a native function in CPython, and as such exhibits the behavior described here:
CPython implementation detail: An implementation may provide built-in
functions whose positional parameters do not have names, even if they
are ‘named’ for the purpose of documentation, and which therefore
cannot be supplied by keyword. In CPython, this is the case for
functions implemented in C that use PyArg_ParseTuple() to parse their
arguments.
str.split is a builtin method implemented in C. Unfortunately some builtin functions/methods do not accept keyword arguments. See this bug report.
In the python documenation, it says:
Any function argument, no matter non-optional or optional (with default value) can be called as keyword argument as long as one of the argument names matches. Keyword argument, however, must follow all positional arguments.
I tried this out:
kwargs = {'step':-1, 'start':10, 'stop':5}
list(range(**kwargs))
But python gives men an error:
TypeError: range() takes no keyword arguments
Why is this?
range() is not a python function. It is a C type; C types follow different rules for arguments and range() only accepts positional arguments.
See the Calls expressions documentation:
CPython implementation detail: An implementation may provide built-in functions whose positional parameters do not have names, even if they are ‘named’ for the purpose of documentation, and which therefore cannot be supplied by keyword. In CPython, this is the case for functions implemented in C that use PyArg_ParseTuple() to parse their arguments.
The positional parameters of range() are not named so cannot be used as keyword arguments.
As you know by now the real answer is that range is a C function which for some reason does not have the same rules of python (would be nice to know why).
But you can do this instead:
range(*{'start':0,'stop':10,'step':2}.values())
In the python documenation, it says:
Any function argument, no matter non-optional or optional (with default value) can be called as keyword argument as long as one of the argument names matches. Keyword argument, however, must follow all positional arguments.
I tried this out:
kwargs = {'step':-1, 'start':10, 'stop':5}
list(range(**kwargs))
But python gives men an error:
TypeError: range() takes no keyword arguments
Why is this?
range() is not a python function. It is a C type; C types follow different rules for arguments and range() only accepts positional arguments.
See the Calls expressions documentation:
CPython implementation detail: An implementation may provide built-in functions whose positional parameters do not have names, even if they are ‘named’ for the purpose of documentation, and which therefore cannot be supplied by keyword. In CPython, this is the case for functions implemented in C that use PyArg_ParseTuple() to parse their arguments.
The positional parameters of range() are not named so cannot be used as keyword arguments.
As you know by now the real answer is that range is a C function which for some reason does not have the same rules of python (would be nice to know why).
But you can do this instead:
range(*{'start':0,'stop':10,'step':2}.values())
Keywords have to be strings
>>> def foo(**kwargs):
... pass
...
>>> foo(**{0:0})
TypeError: foo() keywords must be strings
But by some black magic, namespaces are able to bypass that
>>> from types import SimpleNamespace
>>> SimpleNamespace(**{0:0})
namespace()
Why? And how? Could you implement a Python function that can receive integers in the kwargs mapping?
Could you implement a Python function that can receive integers in the kwargs mapping?
No, you can't. The Python evaluation loop handles calling functions defined in Python code differently from calling a callable object defined in C code. The Python evaluation loop code that handles keyword argument expansion has firmly closed the door on non-string keyword arguments.
But SimpleNamespace is not a Python-defined callable, it is defined entirely in C code. It accepts keyword arguments directly, without any validation, which is why you can pass in a dictionary with non-string keyword arguments.
That's perhaps a bug; you are supposed to use the C-API argument parsing functions, which all do guard against non-string keyword arguments. SimpleNamespace was initially designed just as a container for the sys.implementation data*, and wasn't really designed for other uses.
There might be other such exceptions, but they'll all be C-defined callables, not Python functions.
* The time.get_clock_info() method also runs an instance of the SimpleNamespace class; it's the only other place that the type is currently used.
SimpleNamespace now rejects integer keyword keys. As Martijn supposed, the original behavior was a bug. It seems that it was fixed by bpo-31655: Validate keyword names in SimpleNamespace constructor in v3.9.0b2, and then backported to 3.6.
No, kwargs cannot be integers. This answer, however, is designed as a (very) short history lesson rather than technical answer (for that, please see #MartijnPierter's answer).
The check was originally added in 2010, in issue 8419 (commit fb88636199c12f63d6c8c89f311cdafc91f30d2f) for Python 3.2 (and I believe Python 2.7.4 as well, but don't quote me on that), and simply checked that call kwargs were strings (and raised a value error if they weren't). It also added PyArg_ValidateKeywordArguments to C-api, which simply performed the above check.
In 2017, issue 29951 changed the error text for Python 3.7 from "keyword arguments must be strings" to "keywords must be strings" in PR 916 (commit 64c8f705c0121a4b45ca2c3bc7b47b282e9efcd8). The error remained a ValueError and it did not change the behaviour in any way, and was simply a small change to the error descriptor.
What is the correct name for operator *, as in function(*args)? unpack, unzip, something else?
In Ruby and Perl 6 this has been called "splat", and I think most people from
those communities will figure out what you mean if you call it that.
The Python tutorial uses the phrase "unpacking argument lists", which is
long and descriptive.
It is also referred to as iterable unpacking, or in the case of **,
dictionary unpacking.
I call it "positional expansion", as opposed to ** which I call "keyword expansion".
The Python Tutorial simply calls it 'the *-operator'. It performs unpacking of arbitrary argument lists.
I say "star-args" and Python people seem to know what i mean.
** is trickier - I think just "qargs" since it is usually used as **kw or **kwargs
One can also call * a gather parameter (when used in function arguments definition) or a scatter operator (when used at function invocation).
As seen here: Think Python/Tuples/Variable-length argument tuples.
I believe it's most commonly called the "splat operator." Unpacking arguments is what it does.
The technical term for this is a Variadic function. So in a sense, that's the correct term without regard to programming language.
That said, in different languages the term does have legitimate names. As others have mentioned, it is called "splat" in ruby, julia, and several other languages and is noted by that name in official documentation. In javascript it is called the "spread" syntax. It has many other names in many other languages, as mentioned in other answers. Whatever you call it, it's quite useful!
For a colloquial name there is "splatting".
For arguments (list type) you use single * and for keyword arguments (dictionary type) you use double **.
Both * and ** is sometimes referred to as "splatting".
See for reference of this name being used:
https://stackoverflow.com/a/47875892/14305096
I call *args "star args" or "varargs" and **kwargs "keyword args".