Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Accepting integers as keys of **kwargs

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?

like image 402
wim Avatar asked Sep 11 '17 21:09

wim


People also ask

What datatype are the * Kwargs stored when passed into a function?

*args collects the positional arguments that are not explicitly defined and store them in a tuple. **kwargs does the same as *args but for keyword arguments. They are stored in a dictionary because keyword arguments are stored as name-value pairs.

What is the correct way to use the Kwargs statement?

Understanding **kwargs The double asterisk form of **kwargs is used to pass a keyworded, variable-length argument dictionary to a function. Again, the two asterisks ( ** ) are the important element here, as the word kwargs is conventionally used, though not enforced by the language.

How do you pass a Kwargs argument in Python?

Python **kwargs In the function, we use the double asterisk ** before the parameter name to denote this type of argument. The arguments are passed as a dictionary and these arguments make a dictionary inside function with name same as the parameter excluding double asterisk ** .

How do you pass the dictionary in Kwargs?

Passing Dictionary as kwargs “ kwargs ” stands for keyword arguments. It is used for passing advanced data objects like dictionaries to a function because in such functions one doesn't have a clue about the number of arguments, hence data passed is be dealt properly by adding “**” to the passing type.


3 Answers

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.

like image 166
Martijn Pieters Avatar answered Oct 21 '22 19:10

Martijn Pieters


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.

like image 24
Kevin Avatar answered Oct 21 '22 20:10

Kevin


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.

like image 42
Minion Jim Avatar answered Oct 21 '22 18:10

Minion Jim