How to Copy a Dictionary and Only Edit the Copy

How do I extract and edit a dict value without editing the dict itself?

Ust the .copy() attribute when assigning the fruit variable.

>>> mydict = {'a': ['apple', 'avocado'], 
'b': ['banana', 'berry'],
'c': ['carrot', 'cucumber']}

>>> fruits = mydict['a'].copy()
>>> fruits.pop(0)

This will give the following results

>>> mydict

{'a': ['apple', 'avocado'],
'b': ['banana', 'berry'],
'c': ['carrot', 'cucumber']}

>>> fruits

['avocado']

How to copy a dict and modify it in one line of code

The simplest way in my opinion is something like this:

new_dict = {**old_dict, 'changed_val': value, **other_new_vals_as_dict}

Understanding dict.copy() - shallow or deep?

By "shallow copying" it means the content of the dictionary is not copied by value, but just creating a new reference.

>>> a = {1: [1,2,3]}
>>> b = a.copy()
>>> a, b
({1: [1, 2, 3]}, {1: [1, 2, 3]})
>>> a[1].append(4)
>>> a, b
({1: [1, 2, 3, 4]}, {1: [1, 2, 3, 4]})

In contrast, a deep copy will copy all contents by value.

>>> import copy
>>> c = copy.deepcopy(a)
>>> a, c
({1: [1, 2, 3, 4]}, {1: [1, 2, 3, 4]})
>>> a[1].append(5)
>>> a, c
({1: [1, 2, 3, 4, 5]}, {1: [1, 2, 3, 4]})

So:

  1. b = a: Reference assignment, Make a and b points to the same object.

    Illustration of 'a = b': 'a' and 'b' both point to '{1: L}', 'L' points to '[1, 2, 3]'.

  2. b = a.copy(): Shallow copying, a and b will become two isolated objects, but their contents still share the same reference

    Illustration of 'b = a.copy()': 'a' points to '{1: L}', 'b' points to '{1: M}', 'L' and 'M' both point to '[1, 2, 3]'.

  3. b = copy.deepcopy(a): Deep copying, a and b's structure and content become completely isolated.

    Illustration of 'b = copy.deepcopy(a)': 'a' points to '{1: L}', 'L' points to '[1, 2, 3]'; 'b' points to '{1: M}', 'M' points to a different instance of '[1, 2, 3]'.

Using .copy() to create a copy of a dictionary that can be editing without altering the original dictionary

You want to create a deep copy of the dict, otherwise the objects referenced within the dict will be shared between the original and the shallow copy (hence changing an element in your second example not working how you want):

import copy

dict1 = dict({'a':[1,2,3],'b':[4,5,6],'c':[7,8,9]})

dict2 = copy.deepcopy(dict1)

Nested dictionaries copy() or deepcopy()?

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.

Assigning a value in a dictionary from another dictionary without pointing to it

You can copy the value (so you doesn't reference the same value) by using the copy() method:

Originaldic = {"Foo":["Somthing",1], "Bar":["action",2], "baz":["otherthing",6]}
listofplayer = ["Bar","Foo"]
Tempdic = {}
for player in listofplayer:
Tempdic[player] = Originaldic[player].copy()
#the problem is likely at this step above

if Tempdic[player][1]==2:
Tempdic[player].pop(0)
#note: I have to use pop here because in reality I have a list of list but tried simplify the code
Tempdic[player] = ["differentaction",5]

print(Tempdic)
#{'Bar': ['differentaction', 5], 'Foo': ['Somthing', 1]}
print(Originaldic)
#{'Foo': ['Somthing', 1], 'Bar': ['action', 2], 'baz': ['otherthing', 6]}

but you can also use deepcopy to do a deep copy to make sure that non of the dictionary shares any data:

from copy import deepcopy

Originaldic = {"Foo":["Somthing",1], "Bar":["action",2], "baz":["otherthing",6]}
listofplayer = ["Bar","Foo"]
Tempdic = {}
for player in listofplayer:
Tempdic[player] = deepcopy(Originaldic[player])
#the problem is likely at this step above

if Tempdic[player][1]==2:
Tempdic[player].pop(0)
#note: I have to use pop here because in reality I have a list of list but tried simplify the code
Tempdic[player] = ["differentaction",5]

print(Tempdic)
#{'Bar': ['differentaction', 5], 'Foo': ['Somthing', 1]}
print(Originaldic)
#{'Foo': ['Somthing', 1], 'Bar': ['action', 2], 'baz': ['otherthing', 6]}


Related Topics



Leave a reply



Submit