0

Suppose I have the following module:

blah.py

a = 1
someDict = {'a' : 1, 'b': 2, 'c' : 3}

In the next python session I get the following:

>>> from blah import a, someDict
>>> a
1
>>> someDict
{'a': 1, 'b': 2, 'c': 3}
>>> a = 100
>>> someDict['a'] = 100
>>> del a, someDict
>>> from blah import a, someDict
>>> a
1
>>> someDict['a']
100
>>> import blah
>>> blah.someDict['a']
100

It appears that when I modify an object that I imported from another module, and then re-import that object, it recovers its original value expressed in the module. But this doesn't apply to values in a dictionary. If I want to recover the original value of someDict after making any modification, I have to close the current python session and open a new one. I find that this is even true if I merely called a function that modifies the dict elements.

Why does this happen? And is there some way I can re-import the dictionary with its original value without starting a new python session?

Bridgeburners
  • 637
  • 5
  • 15
  • 2
    This is the standard Python difference between *rebinding* (which you do to `a`) and *mutating* (which you do to `someDict`. – Daniel Roseman Jan 10 '19 at 21:22
  • 1
    "It appears that when I modify an object that I imported from another module, and then re-import that object, it recovers its original value expressed in the module" - no, it doesn't. `a = 100` doesn't modify any objects. – user2357112 Jan 10 '19 at 21:31
  • You only ever modify the *dict object*, you *cannot modify an `int` object*. You are merely assigning another value to the name `a` in the current module's namespace. That isn't mutation. Read the following: https://nedbatchelder.com/text/names.html – juanpa.arrivillaga Jan 10 '19 at 21:43

1 Answers1

4

Because you denamespaced the dict (with from x import y syntax), you need to do this as a two-step process (three including the necessary imports):

  1. Do import importlib, blah to gain access to the reload function, and the actual module to call it on
  2. Run importlib.reload(blah) to throw away the module cache of blah, and reread it afresh from disk (the fresh version is stored in the cache, so future imports related to blah see the new version)
  3. Run from blah import a, someDict again to pull the refreshed contents of blah

The reason you didn't see a problem with a is that after doing from blah import a, a wasn't special; __main__.a was just another alias to blah.a, but since a = 100 rebinds a to a completely new int anyway (and since ints are immutable, even a += 100 would actually perform a rebinding), you never changed blah.a (you'd have to explicitly do import blah, blah.a = 100 to have that happen).

someDict was a problem because, like a, __main__.someDict and blah.someDict end up as aliases of the same dict, and you mutate that dict, you're not rebinding __main__.someDict itself. If you want to avoid mutating blah's values in the first place, make sure the first modification to someDict rebinds it to a fresh dict, rather than modifying the one it's sharing with blah, e.g. instead of:

someDict['a'] = 100

do:

someDict = {**someDict, 'a': 100}

to make a fresh dict with a copy of blah.someDict, but with the value of 'a' in it replaced with a new value.

ShadowRanger
  • 143,180
  • 12
  • 188
  • 271
  • 2
    I find it potentially confusing to refer mention immutability in this context; the issue is about rebinding, which you could just as easily do to a mutable object (eg if OP had done `someDict = {}`. – Daniel Roseman Jan 10 '19 at 21:24
  • @DanielRoseman: Yeah. I debated whether to keep that bit in there. I decided to leave it because there are cases where the mutability matters, e.g. `x = SOMETHING`, `y = x`, `y += z` only mutates `x` if `SOMETHING` is a mutable type like `list`, but not for immutable types like `int`. Admittedly a bit of a tangent from the OP's specific problem. I decided to rephrase to point out that a += 100 would still have rebound due to immutability, but only as a parenthetical, with the rebinding itself being the actual cause. – ShadowRanger Jan 10 '19 at 21:27
  • 1
    Thanks! Both the proactive and post hoc measures worked for me. And I found the explanation very helpful, including the part about immutable types. – Bridgeburners Jan 10 '19 at 21:33