How to copy a dict and modify it in one line of code
Question:
Very often I need to create dicts that differ one from another by an item or two. Here is what I usually do:
setup1 = {'param1': val1,
'param2': val2,
'param3': val3,
'param4': val4,
'paramN': valN}
setup2 = copy.deepcopy(dict(setup1))
setup2.update({'param1': val10,
'param2': val20})
The fact that there is a point in the program at which setup2
is an identical copy of setup1
makes me nervous, as I’m afraid that at some point of the program life the two lines might get separated, which is a slippery slope towards too many bugs.
Ideally I would like to be able to complete this action in a single line of code (something like this):
setup2 = dict(setup1).merge({'param1': val10,
'param2': val20})
Of course, I can use semicolon to squeeze two commands into one physical line, but this looks pretty ugly to me. Are there other options?
Answers:
Solution
Build a function for that.
Your intention would be clearer when you use it in the code, and you can handle complicated decisions (e.g., deep versus shallow copy) in a single place.
def copy_dict(source_dict, diffs):
"""Returns a copy of source_dict, updated with the new key-value
pairs in diffs."""
result=dict(source_dict) # Shallow copy, see addendum below
result.update(diffs)
return result
And now the copy is atomic, assuming no threads involved:
setup2=copy_dict(setup1, {'param1': val10, 'param2': val20})
Addendum – deep copy
For primitives (integers and strings), there is no need for deep copy:
>>> d1={1:'s', 2:'g', 3:'c'}
>>> d2=dict(d1)
>>> d1[1]='a'
>>> d1
{1: 'a', 2: 'g', 3: 'c'}
>>> d2
{1: 's', 2: 'g', 3: 'c'}
If you need a deep copy, use the copy
module:
result=copy.deepcopy(source_dict) # Deep copy
instead of:
result=dict(setup1) # Shallow copy
Make sure all the objects in your dictionary supports deep copy (any object that can be pickled
should do).
setup2 = dict((k, {'param1': val10, 'param2': val20}.get(k, v))
for k, v in setup1.iteritems())
This only works if all keys of the update dictionary are already contained in setup1
.
If all your keys are strings, you can also do
setup2 = dict(setup1, param1=val10, param2=val20)
setup2 = dict(setup1.items() + {'param1': val10, 'param2': val20}.items())
This way if new keys do not exist in setup1
they get added, otherwise they replace the old key/value pairs.
You can write your own class using UserDict
wrapper, and simply add dict
s like
# setup1 is of Dict type (see below)
setup2 = setup1 + {'param1': val10}
All you have to do is
- Define a new class using
UserDict
as base class
- Implement
__add__
method for it.
Something like :
class Dict(dict):
def __add__(self, _dict):
if isinstance(_dict, dict):
tmpdict = Dict(self)
tmpdict.update(_dict)
return tmpdict
else:
raise TypeError
def __radd__(self, _dict):
return Dict.__add__(self, _dict)
If you just need to create a new dict with items from more than one dict, you can use:
dict(a.items() + b.items())
If both “a” and “b” have some same key, the result will have the value from b.
If you’re using Python 3, the concatenation won’t work, but you can do the same by freezing the generators to lists, or by using the itertools.chain function.
You could use keyword arguments in the dictionary constructor for your updates
new = dict(old, a=1, b=2, c=3)
# You can also unpack your modifications
new = dict(old, **mods)
This is equivalent to:
new = old.copy()
new.update({"a": 1, "b": 2, "c": 3})
Notes
dict.copy()
creates a shallow copy.
- All keys need to be strings since they are passed as keyword arguments.
This is an extension to the nice answer posted by Adam Matan:
def copy_dict(d, diffs={}, **kwargs):
res = dict(d)
res.update(diffs)
res.update(kwargs)
return res
The only difference is the addition of kwargs.
Now one can write
setup2 = copy_dict(setup1, {'param1': val10, 'param2': val20})
or
setup2 = copy_dict(setup1, param1=val10, param2=val20)
The simplest way in my opinion is something like this:
new_dict = {**old_dict, 'changed_val': value, **other_new_vals_as_dict}
I like this line (after from itertools import chain
):
d3 = dict(chain(d1.items(), d2.items()))
(Thanks for juanpa.arrivillaga for the improvement!)
Some good answers above. I came here because I had the same issue. Thought the function solution was the most elegant since the question mentioned "often"
def variant(common, diffs):
"""Create a new dict as a variant of an old one
"""
temp = common.copy()
temp.update(diffs)
return temp
to call it you simply use:
PTX130 = variant(PTX100, {'PA_r': 0.25, 'TX_CAP': 4.2E-10})
which for me says that the PTX130 is a variant of the PTX100 with different PA resistance and TX capacitance.
From Python 3.9, you can use the pipe command (e.g. first_dic | second_dic
) for merging dictionary; it can also be used for returning a new updated dictionary by passing the original dictionary first, and the update as a second dictionary:
setup2 = setup1 | {'param1': val10, 'param2': val20}
Very often I need to create dicts that differ one from another by an item or two. Here is what I usually do:
setup1 = {'param1': val1,
'param2': val2,
'param3': val3,
'param4': val4,
'paramN': valN}
setup2 = copy.deepcopy(dict(setup1))
setup2.update({'param1': val10,
'param2': val20})
The fact that there is a point in the program at which setup2
is an identical copy of setup1
makes me nervous, as I’m afraid that at some point of the program life the two lines might get separated, which is a slippery slope towards too many bugs.
Ideally I would like to be able to complete this action in a single line of code (something like this):
setup2 = dict(setup1).merge({'param1': val10,
'param2': val20})
Of course, I can use semicolon to squeeze two commands into one physical line, but this looks pretty ugly to me. Are there other options?
Solution
Build a function for that.
Your intention would be clearer when you use it in the code, and you can handle complicated decisions (e.g., deep versus shallow copy) in a single place.
def copy_dict(source_dict, diffs):
"""Returns a copy of source_dict, updated with the new key-value
pairs in diffs."""
result=dict(source_dict) # Shallow copy, see addendum below
result.update(diffs)
return result
And now the copy is atomic, assuming no threads involved:
setup2=copy_dict(setup1, {'param1': val10, 'param2': val20})
Addendum – deep copy
For primitives (integers and strings), there is no need for deep copy:
>>> d1={1:'s', 2:'g', 3:'c'}
>>> d2=dict(d1)
>>> d1[1]='a'
>>> d1
{1: 'a', 2: 'g', 3: 'c'}
>>> d2
{1: 's', 2: 'g', 3: 'c'}
If you need a deep copy, use the copy
module:
result=copy.deepcopy(source_dict) # Deep copy
instead of:
result=dict(setup1) # Shallow copy
Make sure all the objects in your dictionary supports deep copy (any object that can be pickled
should do).
setup2 = dict((k, {'param1': val10, 'param2': val20}.get(k, v))
for k, v in setup1.iteritems())
This only works if all keys of the update dictionary are already contained in setup1
.
If all your keys are strings, you can also do
setup2 = dict(setup1, param1=val10, param2=val20)
setup2 = dict(setup1.items() + {'param1': val10, 'param2': val20}.items())
This way if new keys do not exist in setup1
they get added, otherwise they replace the old key/value pairs.
You can write your own class using UserDict
wrapper, and simply add dict
s like
# setup1 is of Dict type (see below)
setup2 = setup1 + {'param1': val10}
All you have to do is
- Define a new class using
UserDict
as base class - Implement
__add__
method for it.
Something like :
class Dict(dict):
def __add__(self, _dict):
if isinstance(_dict, dict):
tmpdict = Dict(self)
tmpdict.update(_dict)
return tmpdict
else:
raise TypeError
def __radd__(self, _dict):
return Dict.__add__(self, _dict)
If you just need to create a new dict with items from more than one dict, you can use:
dict(a.items() + b.items())
If both “a” and “b” have some same key, the result will have the value from b.
If you’re using Python 3, the concatenation won’t work, but you can do the same by freezing the generators to lists, or by using the itertools.chain function.
You could use keyword arguments in the dictionary constructor for your updates
new = dict(old, a=1, b=2, c=3)
# You can also unpack your modifications
new = dict(old, **mods)
This is equivalent to:
new = old.copy()
new.update({"a": 1, "b": 2, "c": 3})
Notes
dict.copy()
creates a shallow copy.- All keys need to be strings since they are passed as keyword arguments.
This is an extension to the nice answer posted by Adam Matan:
def copy_dict(d, diffs={}, **kwargs):
res = dict(d)
res.update(diffs)
res.update(kwargs)
return res
The only difference is the addition of kwargs.
Now one can write
setup2 = copy_dict(setup1, {'param1': val10, 'param2': val20})
or
setup2 = copy_dict(setup1, param1=val10, param2=val20)
The simplest way in my opinion is something like this:
new_dict = {**old_dict, 'changed_val': value, **other_new_vals_as_dict}
I like this line (after from itertools import chain
):
d3 = dict(chain(d1.items(), d2.items()))
(Thanks for juanpa.arrivillaga for the improvement!)
Some good answers above. I came here because I had the same issue. Thought the function solution was the most elegant since the question mentioned "often"
def variant(common, diffs):
"""Create a new dict as a variant of an old one
"""
temp = common.copy()
temp.update(diffs)
return temp
to call it you simply use:
PTX130 = variant(PTX100, {'PA_r': 0.25, 'TX_CAP': 4.2E-10})
which for me says that the PTX130 is a variant of the PTX100 with different PA resistance and TX capacitance.
From Python 3.9, you can use the pipe command (e.g. first_dic | second_dic
) for merging dictionary; it can also be used for returning a new updated dictionary by passing the original dictionary first, and the update as a second dictionary:
setup2 = setup1 | {'param1': val10, 'param2': val20}