The difference between ` d3 = {**d1, **d2}` and `d4 = dict(**d1, **d2)`?

Viewed 334

Say there are two dicts, and some of key them are dulpicated. Then I want to combine these two dict, and use the key of d2 to replace d1.

d1 = {
    "2222": {1:3},
    "3333":{1:5}
}

d2 = {
    "2222": {1:5},
    "4444":"a"
}


# expected output is
# {'2222': {1: 5}, '3333': {1: 5}, '4444': 'a'}

I tried with



d3 = {**d1, **d2}  # works

d4 = dict(**d1, **d2)  # failed

TypeError: type object got multiple values for keyword argument '2222'


I'm confused why d3 = {**d1, **d2}worked but d4 = dict(**d1, **d2) failed and what the ** means in these two expression?

3 Answers

They do slightly different things.

The first is similar to writing

d3 = {"2222": {1:3}, "3333":{1:5}, "2222": {1:5}, "4444":"a"}

where you're defining a dictionary with a key repeated. This is permissible (but probably not a good idea). The dictionary will use the last value provided for a repeated key.

The second is similar to writing

d4 = dict("2222"={1:3}, "3333"={1:5}, "2222"={1:5}, "4444"="a")

Here you're calling dict passing keywords, and a keyword is repeated twice. This is forbidden on the language level. (Trying to use "2222" as a keyword argument is also normally forbidden, but it works with ** notation. Opinions differ on whether allowing this was a good idea.)

  • ** notation in python, called kwarg (for keyword argument), implies that the argument is a dictionary.

  • d3 = {**d1, **d2} is a "trick" in python to merge dictionaries.

  • dict() is a built-in method that creates a dictionary, the only possible forms for its constructor are :
dict(**kwarg)
dict([mapping, **kwarg])
dict([iterable, **kwarg])
  • d3 = dict(**d1, **d2) is not possible as same as d3 = dict(d1, d2) isn't.

Finally, the answer lies within the dict() method constructor.

** is Python unpacking operator for dictionaries.

It takes all the key-value pairs (or kwargs short for keyword arguments) inside the dictionary and return function arguments (key-value pairs).

When you unpack two dictionaries d1 and d2 inside curly brackets: {}, what happens is they get unpacked inside a dictionary structure.

According to Python's official documentation:

In dictionaries, later values for the same key will always override earlier ones. In other words d1 and d2 are shallowly merged.

  • That's why, when you run:

    d3 = {**d1, **d2}

the result you get is:

{'2222': {1: 5}, '3333': {1: 5}, '4444': 'a'}
  • if you tried:

    d3 = {**d2, **d1}

result you get would be:

{'2222': {1: 3}, '3333': {1: 5}, '4444': 'a'}

The most recent value prevails.

Solution

dict() function has many internal nuances regarding how it handles unpacking of dictionaries.

dict(**d1, **d2) 

will attempt to overwrite nested dictionaries instead of merging them, hence the TypeError occurs trying to map multiple values to the same key while constructing the dictionary.

Instead using:

dict(d1, **d2)

should solve your problem as it passes the second dict to the constructor as keyword arguments rather than unpacking all the arguments and trying to construct a dictionary by overwriting the nested key-value pairs.

And if you'd like to keep the values from d1, you can instead use:

dict(d2, **d1)
Related