Unpacking multiple keyword arguments
Frequently, you ended up in a situation where you had to provide arguments to a function from a dictionary. If you've ever faced that need, you probably also ended up in a case where you had to take the arguments from multiple dictionaries.
Generally, Python functions accept arguments from a dictionary through unpacking (the **
syntax), but so far, it hasn't been possible to use unpacking twice in the same call, nor was there an easy way to merge two dictionaries.
How to do it...
The steps for this recipe are:
- Given a function,
f
, we want to pass the arguments from two dictionaries,d1
andd2
as follows:
>>> def f(a, b, c, d): ... print (a, b, c, d) ... >>> d1 = dict(a=5, b=6) >>> d2 = dict(b=7, c=8, d=9)
collections.ChainMap
can help us achieve what we want; it can cope with duplicated entries and works with any Python version:
>>> f(**ChainMap(d1, d2))
5 6 8 9
- In Python 3.5 and newer versions, you can also create a new dictionary by combining multiple dictionaries through the literal syntax, and then pass the resulting dictionary as the argument of the function:
>>> f(**{**d1, **d2})
5 7 8 9
- In this case, the duplicated entries are accepted too, but are handled in reverse order of priority to
ChainMap
(so right to left). Notice howb
has a value of7
, instead of the6
it had withChainMap
, due to the reversed order of priorities.
This syntax might be harder to read due to the amount of unpacking operators involved, and with ChainMap
it is probably more explicit what's happening for a reader.
How it works...
As we already know from the previous recipe, ChainMap
looks up keys in all the provided dictionaries, so it's like the sum of all the dictionaries. The unpacking operator (**
) works by inviting all keys to the container and then providing an argument for each key.
As ChainMap
has keys resulting from the sum of all the provided dictionaries keys, it will provide the keys contained in all the dictionaries to the unpacking operator, thus allowing us to provide keyword arguments from multiple dictionaries.
There's more...
Since Python 3.5 through PEP 448, it's now possible to unpack multiple mappings to provide keyword arguments:
>>> def f(a, b, c, d):
... print (a, b, c, d)
...
>>> d1 = dict(a=5, b=6)
>>> d2 = dict(c=7, d=8)
>>> f(**d1, **d2)
5 6 7 8
This solution is very convenient, but has two limits:
- It's only available in Python 3.5+
- It chokes on duplicated arguments
If you don't know where the mappings/dictionaries you are unpacking come from, it's easy to end up with the issue of duplicated arguments:
>>> d1 = dict(a=5, b=6)
>>> d2 = dict(b=7, c=8, d=9)
>>> f(**d1, **d2)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: f() got multiple values for keyword argument 'b'
In the previous example, the b
key is declared in both d1
and d2
, and that causes the function to complain that it received duplicate arguments.