427
votes

I would like to make a deep copy of a dict in python. Unfortunately the .deepcopy() method doesn't exist for the dict. How do I do that?

>>> my_dict = {'a': [1, 2, 3], 'b': [4, 5, 6]}
>>> my_copy = my_dict.deepcopy()
Traceback (most recent calll last):
  File "<stdin>", line 1, in <module>
AttributeError: 'dict' object has no attribute 'deepcopy'
>>> my_copy = my_dict.copy()
>>> my_dict['a'][2] = 7
>>> my_copy['a'][2]
7

The last line should be 3.

I would like that modifications in my_dict don't impact the snapshot my_copy.

How do I do that? The solution should be compatible with Python 3.x.

6
I don't know if it's a duplicate, but this: stackoverflow.com/questions/838642/python-dictionary-deepcopy is awfully close.charleslparker

6 Answers

607
votes

How about:

import copy
d = { ... }
d2 = copy.deepcopy(d)

Python 2 or 3:

Python 3.2 (r32:88445, Feb 20 2011, 21:30:00) [MSC v.1500 64 bit (AMD64)] on win32
Type "help", "copyright", "credits" or "license" for more information.
>>> import copy
>>> my_dict = {'a': [1, 2, 3], 'b': [4, 5, 6]}
>>> my_copy = copy.deepcopy(my_dict)
>>> my_dict['a'][2] = 7
>>> my_copy['a'][2]
3
>>>
46
votes

dict.copy() is a shallow copy function for dictionary
id is built-in function that gives you the address of variable

First you need to understand "why is this particular problem is happening?"

In [1]: my_dict = {'a': [1, 2, 3], 'b': [4, 5, 6]}

In [2]: my_copy = my_dict.copy()

In [3]: id(my_dict)
Out[3]: 140190444167808

In [4]: id(my_copy)
Out[4]: 140190444170328

In [5]: id(my_copy['a'])
Out[5]: 140190444024104

In [6]: id(my_dict['a'])
Out[6]: 140190444024104

The address of the list present in both the dicts for key 'a' is pointing to same location.
Therefore when you change value of the list in my_dict, the list in my_copy changes as well.


Solution for data structure mentioned in the question:

In [7]: my_copy = {key: value[:] for key, value in my_dict.items()}

In [8]: id(my_copy['a'])
Out[8]: 140190444024176

Or you can use deepcopy as mentioned above.

24
votes

Python 3.x

from copy import deepcopy

my_dict = {'one': 1, 'two': 2}
new_dict_deepcopy = deepcopy(my_dict)

Without deepcopy, I am unable to remove the hostname dictionary from within my domain dictionary.

Without deepcopy I get the following error:

"RuntimeError: dictionary changed size during iteration"

...when I try to remove the desired element from my dictionary inside of another dictionary.

import socket
import xml.etree.ElementTree as ET
from copy import deepcopy

domain is a dictionary object

def remove_hostname(domain, hostname):
    domain_copy = deepcopy(domain)
    for domains, hosts in domain_copy.items():
        for host, port in hosts.items():
           if host == hostname:
                del domain[domains][host]
    return domain

Example output: [orginal]domains = {'localdomain': {'localhost': {'all': '4000'}}}

[new]domains = {'localdomain': {} }}

So what's going on here is I am iterating over a copy of a dictionary rather than iterating over the dictionary itself. With this method, you are able to remove elements as needed.

-2
votes

I am just learning Python (3.7.7 as an expression evaluator part of a REPL interface software environment) today, and I have found that there is a frozenset that might be useful for this purpose. I learned about it at this answer: https://stackoverflow.com/a/11251781/832705

some documentation should be here: https://docs.python.org/3/library/stdtypes.html?highlight=frozenset#frozenset

My thought is that the frozenset should be like a deep copy enough for purposes of iteration of a list/dictionary type object that has a tendency to throw "RuntimeError: dictionary changed size during iteration" errors during iteration.

I mean to get back to learning about frozenset more later. If you vote this answer down, because it is wrong, not applicable, or for another reason, it would at least remind me to do more learning.

-3
votes

I like and learned a lot from Lasse V. Karlsen. I modified it into the following example, which highlights pretty well the difference between shallow dictionary copies and deep copies:

    import copy

    my_dict = {'a': [1, 2, 3], 'b': [4, 5, 6]}
    my_copy = copy.copy(my_dict)
    my_deepcopy = copy.deepcopy(my_dict)

Now if you change

    my_dict['a'][2] = 7

and do

    print("my_copy a[2]: ",my_copy['a'][2],",whereas my_deepcopy a[2]: ", my_deepcopy['a'][2])

you get

    >> my_copy a[2]:  7 ,whereas my_deepcopy a[2]:  3
-10
votes

A simpler (in my view) solution is to create a new dictionary and update it with the contents of the old one:

my_dict={'a':1}

my_copy = {}

my_copy.update( my_dict )

my_dict['a']=2

my_dict['a']
Out[34]: 2

my_copy['a']
Out[35]: 1

The problem with this approach is it may not be 'deep enough'. i.e. is not recursively deep. good enough for simple objects but not for nested dictionaries. Here is an example where it may not be deep enough:

my_dict1={'b':2}

my_dict2={'c':3}

my_dict3={ 'b': my_dict1, 'c':my_dict2 }

my_copy = {}

my_copy.update( my_dict3 )

my_dict1['b']='z'

my_copy
Out[42]: {'b': {'b': 'z'}, 'c': {'c': 3}}

By using Deepcopy() I can eliminate the semi-shallow behavior, but I think one must decide which approach is right for your application. In most cases you may not care, but should be aware of the possible pitfalls... final example:

import copy

my_copy2 = copy.deepcopy( my_dict3 )

my_dict1['b']='99'

my_copy2
Out[46]: {'b': {'b': 'z'}, 'c': {'c': 3}}