Search code examples
pythonglobal-variablespython-module

Unable to see changes to a global variable from a different Python module


Consider the following Python application of two files, namely app.py:

#!/usr/bin/env python3
import other


MY_DICT = {'initial': 10}


def main():
    print('before main:', MY_DICT)
    MY_DICT['foo'] = 20
    MY_DICT['bar'] = 30
    print('after changes:', MY_DICT)
    other.check_dict()
    print('back in main:', MY_DICT)


if __name__ == '__main__':
    main()

and other.py:

def check_dict():
    from app import MY_DICT
    print('inside other:', MY_DICT)

I would like to make MY_DICT from app.py visible in other.py. It is visible, but I cannot change it. Here is the output if I run ./app.py with Python 3.8.0:

before main: {'initial': 10}
after changes: {'initial': 10, 'foo': 20, 'bar': 30}
inside other: {'initial': 10}
back in main: {'initial': 10, 'foo': 20, 'bar': 30}

Not only MY_DICT is back to its initial state when inspected from other.py, it's back to the changed state when I return to main.py. What is happening here and how do I make it the a real global variable?

I've already read this question and ensured that I do re-import MY_DICT in check_dict.


Solution

  • Solution: don't do that. Do not import the entry point Python script as a module. Create a separate small file which is never imported by anyone else and is intended to be run from the shell.

    When you run the app as ./app.py Python actually loads your app.py as a module named __main__. But your other.py says import app, so it imports a separate module named app, which is only coincidentally loaded from the same source file app.py.

    Now you have two instances of app.py loaded in your program, each one having a separate MY_DICT. Your __main__ module sees its own sys.modules['__main__'].MY_DICT, but other module accesses sys.modules['a'].MY_DICT. You can check this by printing id(MY_DICT) in both main() and check_dict(): they are different. See more details here.

    Technically, you can import __main__ as app in other.py. However, that will break the moment someone tries to use other.py in any other way which does not involve starting ./app.py.