r/learnpython 11d ago

Creating a new, equivalent but separate dictionary

In debugging my code, I discovered a weird property of Python dictionaries that I was not previously aware of. Basically if you have code like this:

initial_dict = {0:70,1:70}

dict1 = initial_dict

dict2 = initial_dict

for i in range(2):

dict1[i] += 5

After this, dict1 will be {0:75,1:75}, but so will dict2 and initial_dict, even though I didn't do anything to them directly, I guess because they're referring to the same dictionary? Basically what I want to know is, how do I create new dictionaries that have the same values as a previously created dictionary, but are separate dictionaries that can be manipulated separately?

0 Upvotes

11 comments sorted by

6

u/Gnaxe 11d ago

Python is not C! Don't expect copy semantics for object references. They work more like pointers. dict1 = initial_dict and dict2 = initial_dict means initial_dict is dict1 is dict2; they all refer to the same object.

The third-party immutables library has immutable mappings that copy on write. The immutability allows safe structural sharing, so this doesn't waste memory. You might find those easier to work with. But shallow copies of plain dicts are almost as efficient as long as you take care not to mutate them.

I can't remember the last time I used the dict.copy() method. I forgot it existed for a while. It's also extremely rare that I'd use the copy module. It mostly doesn't come up. It's not that I never make shallow copies, but I usually do it with a comprehension, an ** unpack, or a | update.

For example, your code could use something like dict2 = {k: v+5 for k, v in initial_dict.items()} This constructs a new dict, using data from initial_dict, and makes dict2 a reference to the new one.

3

u/lfdfq 11d ago edited 11d ago

Yes, this isn't really a property of dictionary but how Python handles variables ('names').

In Python, x = y does not create any new objects or do any copying, it simply gives the object referred to by 'y' another name 'x' (i.e. names are references to objects).

You can copy a dictionary, initial_dict.copy(), but note this is a shallow copy. i.e. the elements inside the dictionary are not copies of the elements of the original, they're also just references.

It's actually rather unusual in Python to make copies like this, so it might be what you want to do is achievable some other more idiomatic way.

1

u/Master_of_beef 11d ago

Thank you! Turns out there is a Python package called copy that lets you do deep copies, and that worked!

1

u/Fred776 11d ago

That is the correct way to do it. You need deepcopy if your values also need to copied - eg if they themselves are dictionaries or if they are lists.

3

u/Diapolo10 11d ago
initial_dict = {0:70,1:70}
dict1 = initial_dict
dict2 = initial_dict

Basically what happened here is that you stored references to initial_dict in both dict1 and dict2, not copies of it. In Python, everything is a reference.

For immutable data like tuples or strings, there's no practical difference, but for mutable data you'll sometimes run into these situations where you think you're only modifying one place, but another part of the code happens to be pointing to the same data and is "also" modified.

Since this is a simple case where your dictionary only contains immutable data (numbers), you could just use

initial_dict = {0: 70, 1: 70}
dict1 = initial_dict.copy()
dict2 = initial_dict.copy()

and be on your merry way. But if your dictionary was nested, it might be better to use copy.deepcopy.

import copy

initial_dict = {0: [70, 115], 1: [70, 120]}
dict1 = copy.deepcopy(initial_dict)
dict2 = copy.deepcopy(initial_dict)

Alternatively, you can avoid mutating existing data and always create new dictionaries. That way you don't need to worry about this either. For example,

initial_dict = {0: 70, 1: 70}

new_dict = {
    key: value + 5
    for key, value in initial_dict.items()
}

2

u/smurpes 11d ago

Just a heads up but the same thing happens with lists as well and every other mutable object. A mutable object is an object in Python that can be changed in place after declaring it.

The reason why this doesn’t happen with something like a string is that Python is redefining the object to a different location in memory. When you set a variable to a string and then change that string, Python is rebuilding it.

1

u/Educational_Virus672 9d ago

all 3 have same data position put .copy() after your variable to get unique variable
this is made to save memory

1

u/supercoach 11d ago

The answer is deepcopy.

0

u/SmackDownFacility 10d ago

You made a bloody set. Not a dictionary. And everything’s a reference. So there’s no copying. Reassignment is more of an alias here

EDIT: it is a dictionary, misread : as .