user1089097
user1089097

Reputation: 145

Best way to partially deep copy a dictionary in Python?

I have a graph that is represented as a dict where each key is an instance of a (self-defined) Node class and the values are sets of instances of Nodes. So, basically, the graph looks something like this, but way larger:

g = { Node1 : {Node2, Node3}, Node2 : {Node4}, Node3: set(), Node4 : {Node1} }

In very short words of what I want to do: I want to arbitrarily change a dictionary and then revert it to its original value before those changes.

I have a series of transformations that I can make to this graph, but I don't have any possible way of knowing beforehand which transformation that I want to do. The transformation will always:

Since I don't know which transformation I want to do, I want to perform every transformation to the same graph and then pick one.

An obvious idiom could be:

This does not work because copying the dictionary only performs a shallow copy, so all changes to g are also performed on temp.

I can use deepcopy, but even that cannot work as is. I want to deepcopy the actual dictionary g, but the keys and elements in the values of g (the Nodes) cannot be deepcopied, because in my transformation, I will look for elements that are in g but not in temp because they are now new objects.

I can attempt to redefine deepcopy for the Node class, which works, but it is very hackish and I do need to use deepcopy to perform my transformations t.

Following is a minimal working example that does what I want it to do -- but in a way that I do not want to because then I need to create a workaround for making deepcopy actually work when I need it to. Also, the code to do the transformations is all using g and the actual Nodes in g as it stands right now, so not having to change much of that would be nice.

Is there a better way to do this? (all prints should be True)

from copy import deepcopy

class MyClass:

    def __init__(self, value):
        self.value = value

    def __repr__(self):
        return str(self.value)

    def __deepcopy__(self, memo):
    # do not rly deep copy plx
        return self

    class1 = MyClass(1)
    class2 = MyClass(2)
    class3 = MyClass(3)

    dict_class = { class1 : {class2, class3}, class2 : {class3}, class3 : {class1}}

    #### Want to create a copy of the dictionary with exact same objects... ####

    dict_class_copy = deepcopy(dict_class)

    print(dict_class)
    print(dict_class_copy)

    print(class1 in dict_class and class2 in dict_class and class3 in dict_class)
    print(class1 in dict_class_copy and class2 in dict_class_copy and class3 in dict_class_copy)

    dict_class.pop(class2)
    dict_class[class1].remove(class3)

    # don't want to change original dictionary key values
    print(class2 not in dict_class and class2 in dict_class_copy)

    # this breaks w/o deepcopy!
    print(class3 not in dict_class[class1] and class3 in dict_class_copy[class1])

    # reassign it back 
    dict_class = dict_class_copy 

    # i want this back!
    print(class2 in dict_class)
    print(class3 in dict_class[class1])

Upvotes: 1

Views: 934

Answers (1)

RootTwo
RootTwo

Reputation: 4418

Make a "1 level deep" copy of the dict by copying the keys and making copies of the sets. Replace this:

dict_class_copy = deepcopy(dict_class)

with this:

dict_class_copy = {k:set(v) for k,v in dict_class.items()}

and your tests pass.

Upvotes: 1

Related Questions