1

There are two dictionaries: a and b. b is created using a, but it does not point to the same object as a. (a is b returns False)

I have observed that when I update a value in b: it changes the corresponding value of a (not by the same update that happened in b though). What's more troubling is that, this is only observed when using the tf.keras.optimizers.get function to update b. When used some other method to update b, this behavior is not observed.

Also, the change applied to a b's element isn't applied to a's corresponding element, but rather an augmentation of a's corresponding element is resulted - a lower cased dictionary value, while no lowercase() function was applied.

Reproducible example:

import tensorflow as tf

a = {
    "x": 1,
    'y': {
        'class_name': 'Adam',
        'config': {
            'learning_rate': 0.0001
        }
    }
}  # a['y'] can be passed as an input to tf.keras.optimizers.get
b = {key: value for key, value in a.items()}

print(a)  # b also looks the same
# {'x': 1,
#  'y': {'class_name': 'Adam', 'config': {'learning_rate': 0.0001}}}

Update the 'y' value of b:

b['y'] = tf.keras.optimizers.get(b['y'])

As expected, b is changed:

print(b)
# {'x': 1,
#  'y': <tensorflow.python.keras.optimizer_v2.adam.Adam at 0x7f5656476090>}

But, this has also changed the value of a['y']['class_name'] to lower case!

print(a)
# {'x': 1,
#  'y': {'class_name': 'adam', 'config': {'learning_rate': 0.0001}}}

But no change is observed in a when b['x'] is updated like below:

b['x'] = b['x'] * 2

print(a)
# {'x': 1,
#  'y': {'class_name': 'adam', 'config': {'learning_rate': 0.0001}}}

print(b)
# {'x': 2,
 'y': <tensorflow.python.keras.optimizer_v2.adam.Adam at 0x7f44374ad610>}

Why is it so? How to avoid it?

Edit 1:

After comments about shallow copy:

a['x'] is b['x'], a['y'] is b['y']
# (True, True)  # Edit 1.1 - executed just after the definitions of a and b

Edit 2:

Regarding the Close vote:

I do not understand why a['y']['class_name'] went from 'Adam' to 'adam' (lower case). This is not discussed in the suggested duplicate.

No lowercase() function was applied to any of the elements in either dictionary.

akilat90
  • 5,436
  • 7
  • 28
  • 42
  • 1
    Note, `b = {key: value for key, value in a.items()}` is an overly complicated way of writing `b = dict(a)`. In any case, you are creating a *shallow copy* of `a`. – juanpa.arrivillaga Jul 14 '21 at 21:28
  • It’s a feature; when you copy the y value from a to be they still reference the same dictionary and if you change either the other reference will of course reflect that change. – DisappointedByUnaccountableMod Jul 14 '21 at 21:28
  • Seach for copy.deepcopy in the documentation - this is one way of making new dictionaries. – DisappointedByUnaccountableMod Jul 14 '21 at 21:30
  • @barny: `.. if you change either the other reference will of course reflect that change` - this isn't happening here! `b['x'] = b['x'] * 2` hasn't affected `a['x']`, but `b['y'] = tf.keras.optimizers.get(b['y'])` has made `a['y']['class_name']` to lower case (`'Adam'` -> `'adam'`, it's not even the same change that `b['y']` got; `b['y']` became `` – akilat90 Jul 14 '21 at 21:38
  • The `x` value is an integer, immutable; the same would happen for a string. But dictionaries and lists are mutable and if you want to not refer to the same object you have to consciously create a new object using `copy.deepcopy` – DisappointedByUnaccountableMod Jul 14 '21 at 21:52
  • Does this answer your question? [List changes unexpectedly after assignment. Why is this and how can I prevent it?](https://stackoverflow.com/questions/2612802/list-changes-unexpectedly-after-assignment-why-is-this-and-how-can-i-prevent-it) – DisappointedByUnaccountableMod Jul 14 '21 at 21:54
  • Also see https://stackoverflow.com/questions/67147408/value-in-python-dictionary-changes-unintentionally-even-when-dictionary-was-not – DisappointedByUnaccountableMod Jul 14 '21 at 21:56
  • @barny Thank you for the links! I still can't grasp how `'Adam'` became `'adam'` though. Can you explain that? – akilat90 Jul 14 '21 at 22:02
  • @juanpa.arrivillaga Thank you! Please check `Edit: 1`. Now, with that output, I do not understand two things: 1). `b['x'] = b['x'] * 2` hasn't changed `a['x']` while `a['x'] is b['x']` is `True`, 2). While `a['y'] is b['y']` returned `False`, changing `b['y']` has applied an unrelated function to `a['y']` (changing `'Adam'` to `'adam'`). – akilat90 Jul 14 '21 at 22:28
  • Sorry, I should've executed them at the beginning. See Edit 1.1, it returns `True, True` when executed just after the definitions of `a` and `b`. – akilat90 Jul 14 '21 at 22:36

0 Answers0