10

I tried to store a dictionary template at the beginning of my code that most of the functions will use:

  • Dictionary: keys = Client name, values = Dictionary2
  • Dictionary2: keys = User name, values = None

I filled it with all our clients and their users. Then each part of the code can copy this dictionary and produces it's owns outputs. The goal is that each output will have the same "base" dictionary structure like a template where None can be modified.

For each process using this dictionnary I use the following :

process1dict = clientdict 
# processing 1
output1dict = ... #modified version of original clientdict, the None values have been replaced by dictionaries/lists

process2dict = clientdict
# processing 2
output2dict = ... #same here but could be different

The problem that I have is that the cliendict changes each time it is copied into a process! I noticed that because of the None value in my initial cliendict it changes after each process (depending on the output of each one of course).

Edit: I found the copy library but copy() seems to not help my case. I will try out the deepcopy() but why did copy() didn't worked? And why deepcopy() will?

Alex
  • 111
  • 1
  • 1
  • 6
  • 1
    Yes, you need to do deepcopy -> without it `clientdictDNT` just points to the same underlying dictionary, so it will be modified when you modify `clientdict` you can also `dict(clientdict)` or `clientdict.copy()` – TemporalWolf Sep 13 '16 at 16:42
  • 1
    You should read this article: [Facts and myths about Python names and values](http://nedbatchelder.com/text/names.html) by SO veteran, Ned Batchelder. – PM 2Ring Sep 13 '16 at 16:44
  • If you don't use deepcopy(), your members of your new dictionaries will point to the dictionaries embedded in your base dictionary. deepcopy() makes copies of the stuff inside of the dictionary being copied. – Rick Sep 13 '16 at 16:54
  • Thanks for the article and answers. If I understand correctly as I use a nested dictionaries which are mutable objects, if I do a ’copy()’ and that I use in my processes some 'changing' functions like ’.append()’ it will link the changes directly to the value itself (in particular here the ’None’ value). If I want to use it like a dictionary template (like I do here) I think it would be better to use ’deepcopy()’. I will give it a try tomorrow! – Alex Sep 13 '16 at 21:53

1 Answers1

18

When you're working with a mutable collection like a dictionary or a list, and you perform an assignment, you are not creating a copy of that object by default – i.e., the assignment of some dict b to another dict a creates a reference from b to the original object a, such that when you mutate b you indirectly also mutate a.

See this basic example:

>>> orig = {"a": 1, "b": 2}
>>> new = orig
>>> new["a"] = 9
>>> orig
{'a': 9, 'b': 2}
>>> new
{'a': 9, 'b': 2}
>>> new is orig
True

To fix this and keep the new and orig dictionaries separate objects that do not reference each other, make a deepcopy of orig when assigning it to new:

>>> import copy
>>> orig = {"a": 1, "b": 2}
>>> new = copy.deepcopy(orig)
>>> new["a"] = 9
>>> orig
{'a': 1, 'b': 2}
>>> new
{'a': 9, 'b': 2}
>>> new is orig
False

Also, here's a tl;dr for the Python documentation linked to above:

Assignment statements in Python do not copy objects, they create bindings between a target and an object. For collections that are mutable or contain mutable items, a copy is sometimes needed so one can change one copy without changing the other.

Friendly King
  • 2,396
  • 1
  • 23
  • 40
  • 1
    in many cases a deepcopy isn't necessary, and can actually be harmful. If you have an object `User("Somebody")` that belongs to two clients, you probably DON'T want to split those into two equivalent objects and mutate them separately. If you don't want or need `copy.deepcopy`, you can use `dict.copy` instead. `orig = {"a": 1, "b": 2}; new = orig.copy()` – Adam Smith Sep 13 '16 at 18:41
  • 1
    In this particular case I don't have User Name objects that belong to two clients. The problem that I have now is that this ’clientdict’ dictionary was used as template and the ’None’ value was used as blank character to fill the rest of the dictionary in the processes. When I first tried to use ’copy()’ which was at the last minute of writing the question, I found out that it didn't worked and I think I understand why now thanks to your comments! Thanks guys – Alex Sep 13 '16 at 21:59
  • 1
    #upvote. This was deep ;) and helpful – niCk cAMel Feb 13 '18 at 11:26
  • 1
    You save my night bud ! thanks =) – ykatchou Dec 22 '21 at 21:43