0

I have a problem where I have a function that may or may not alter the values of a dictionary, and I want to keep track of those alterations with another function. It looks something like this:

dict_t0 = my_dict
function_that_alters_values(my_dict)
dict_t1 = my_dict
compare_dicts(dict_t0,dict_t1)

However, since dict_t0 simply points to the object my_dict, any changes made to my_dict would be applied to dict_t0, which is why I tried using

dict_t0 = deepcopy(my_dict)

However, this gives me the following error:

RecursionError: maximum recursion depth exceeded

Is there a better way to solve my problem?

Jaroslav Bezděk
  • 6,967
  • 6
  • 29
  • 46
  • 2
    dictionaries have a .copy() method, have you tried that instead of deepcopy? – Dan Aug 20 '19 at 10:18
  • @Dan copy() will create a shallow copy & not a full copy. So the values are not separated. Do you think the changes in values will be reflected if copy() is used? – Kris Aug 20 '19 at 10:27
  • 1
    If the built-in .copy() method doesn't solve it, you can use the pickle library to save the initial state of your dictionary: https://docs.python.org/3/library/pickle.html#module-pickle – Dan Aug 20 '19 at 10:32
  • A side note from `copy` docs for `deepcopy`: "Recursive objects (compound objects that, directly or indirectly, contain a reference to themselves) may cause a recursive loop." This may explain the maximum recursion depth issue. – hilberts_drinking_problem Aug 20 '19 at 10:37
  • Based on Dan's suggestion, you could also look [here](https://stackoverflow.com/questions/1410615/copy-deepcopy-vs-pickle). – hilberts_drinking_problem Aug 20 '19 at 10:39
  • is there a loop reference in a dict like d[a][b] is d? – snamef Aug 20 '19 at 12:24

2 Answers2

2

Maybe you can change the function, so it doesn't work inplace, rather to return new instance:

original = {1: [1, 2, 3], 2: [4, 5, 6]}
new = original

def squareValues(someDict):
    return {k: [i**2 for i in v] for k, v in someDict.items()}

new = squareValues(new)

original
#{1: [1, 2, 3], 2: [4, 5, 6]}

new
#{1: [1, 4, 9], 2: [16, 25, 36]}

EDIT

As for the deepcopy you can increase the recursion limit:

sys.setrecursionlimit(5000) # or more if your structure is deeper.

To see the current recursion limit use:

sys.getrecursionlimit()
zipa
  • 27,316
  • 6
  • 40
  • 58
0

For the deepcopy problem you could follow @zipa answer. Then for comparing both dicts you can create a function like this:

def compare_dicts(d1, d2):
    """
    Gives the actions necessary for d1 to be equal to d2
    """
    diff = {
      "add": [key for key in d2 if key not in d1],
      "delete": [key for key in d1 if key not in d2],
      "update": {key: d2[key] for key in set(d1) & set(d2) if d1[key] != d2[key]}
    }
    return diff

Usage example:

dict1 = {
  "a": 1, # same
  "b": 2, # different value
  "c": 3, # not present
  #"d": 4 missing
}

dict2 = {
  "a": 1,
  "b": 1,
  "d": 4
}
print(compare_dicts(dict1, dict2))
{
  'add': ['d'],
  'delete': ['c'],
  'update': {'b': 1}
}
palvarez
  • 1,508
  • 2
  • 8
  • 18