WoJ
WoJ

Reputation: 30044

How to change the value of a module variable from within an object of another module?

Summary: I have a script which exposes(1) a dict and I need to modify this dict from with another module

Note: Two great answers to a similar question explain a lot about variables scoping between modules but I fail to understand how this applies to my case.

In the code below, I expected that I will be able to modify the mainmodule.py variable container from within submodule.py, which is not the case. Why?

How should I address the mainmodule instance of container from within submodule?

The code for the main script

# mainmodule.py
# the main script which ultimately exposes the module variable 'container'
import submodule

container = dict()

class MainClass:
    def __init__(self):
        self.sub = submodule.SubClass()

    def set(self, number):
        print("main: container was {container}".format(container=container))
        container['x'] = number
        print("main: container is {container}".format(container=container))


    def call(self, number):
        self.sub.set(number)

if __name__ == "__main__":
    mc = MainClass()
    # updating container from this script
    mc.set(1)
    # updating container from another module
    mc.call(2)
    # again from this script, to check the updates
    mc.set(3)

The code for the imported module

# submodule.py
import mainmodule

class SubClass:
    def __init__(self):
        pass

    def set(self, number):
        print("sub: container was {container}".format(container=mainmodule.container))
        mainmodule.container['x'] = number
        print("sub: container is {container}".format(container=mainmodule.container))

The output is

main: container was {}
main: container is {'x': 1}
sub: container was {}
sub: container is {'x': 2}
main: container was {'x': 1}
main: container is {'x': 3}

(1)The actual code uses bottle to provide container via json.dumps()

Upvotes: 3

Views: 1291

Answers (1)

SingleNegationElimination
SingleNegationElimination

Reputation: 156278

Staring at your code for a bit, I think i know what might be throwing you off. The python script invoked as python foo.py will end up being a module (in sys.modules) called __main__. That means the bottom bit of your mainmodule.py shows is loaded and compiled and run once, with __name__ == "__main__", which causes some things to happen. That module imports submodule, which has not been imported yet, so that gets loaded and run.

submodule in turn tries to import mainmodule. Although that file has been executed before, it's not known to the interpreter by that module name, so mainmodule.py gets run again, this time with __name__ == "mainmodule" (which isn't the same as "__main__", so the if suite at the bottom is skipped).

That means you have two copies of container, one in the module who's name is __main__, and one in a module named mainmodule. The fact that both are from a file called ./mainmodule.py is not relevant.

There are a few ways to fix this. One is to always import the real immediately, as in:

# mainmodule.py
import submodule
class Foo:
    pass
if __name__ == "__main__":
    import mainmodule
    mainmodule.Foo()

Another option is to move the code inside the if to another file.

Upvotes: 5

Related Questions