-2

Need a solution or workaround to read and write the variable in moduleA from moduleB please. Here is the code:

moduleA

import moduleB

variable = 10

def changeVariable():
    global variable
    variable = 20

def main():
    print("From moduleA, variable =", variable, " before change.")
    moduleB.main()
    print("From moduleA, variable =", variable, " after change.")

if __name__ == "__main__":
    main()

moduleB

import moduleA

def main():
    print("From moduleB, variable =", moduleA.variable, " before change.")
    moduleA.variable = 20 # Try 1
    moduleA.changeVariable() # Try 2
    print("From moduleB, variable =", moduleA.variable, " after change.")

if __name__ == "__main__":
    main()

When running moduleA we get:

  • From moduleA, variable = 10 before change.
  • From moduleB, variable = 10 before change.
  • From moduleB, variable = 20 after change.
  • From moduleA, variable = 10 after change.

Here is another example using static variables:

moduleAA

import moduleBB

class AA:
    variable = 0

    @staticmethod
    def changeVariable():
        AA.variable = 20

def main():
    AA.variable = 10
    print("From moduleAA, variable =", AA.variable, " before change.")
    moduleBB.main()
    print("From moduleAA, variable =", AA.variable, " after change.")

if __name__ == "__main__":
    main()

moduleBB

import moduleAA

def main():
    print("From moduleB, variable =", moduleAA.AA.variable, " before change.")
    moduleAA.AA.variable = 20 # Try 1
    moduleAA.AA.changeVariable() # Try 2
    print("From moduleB, variable =", moduleAA.AA.variable, " after change.")

if __name__ == "__main__":
    main()

When running moduleAA we get:

  • From moduleAA, variable= 10 before change.
  • From moduleBB, variable= 0 before change.
  • From moduleBB, variable= 20 after change.
  • From moduleAA, variable= 10 after change.
Yster
  • 3,147
  • 5
  • 32
  • 48
  • 3
    I already [explained](https://stackoverflow.com/questions/44550783/change-global-module-variable-from-another-module) what's going on the last time you posted this. The solution is to stop relying on cyclic imports, global variables, and especially cross-module global variable dependencies. All of those things cause fragile, hard-to-reason-about code. – user2357112 Jun 14 '17 at 19:47
  • @user2357112 Thank you very much for the info. I don't understand how to solve this yet. So what you are saying is that it is impossible to modify this variable? I have a module containing an asynchronous API and another module containing the GUI. I need at least one dictionary shared between the two. This dictionary should be static. – Yster Jun 14 '17 at 20:32
  • The question of how to share data between modules has been asked [many](https://stackoverflow.com/questions/24875798/python-sharing-variables-between-modules), [many](https://stackoverflow.com/questions/14247974/python-how-to-share-a-variable-between-two-modules) [times](https://stackoverflow.com/questions/3338283/python-sharing-global-variables-between-modules-and-classes-therein). (Those just happened to be the first three I stumbled upon. There are plenty more.) Did you even *try* searching for "share variable between python modules"? – John Y Jun 14 '17 at 22:26
  • @John Y Thanks for the comment! I have searched for many things but not your exact phrase. I found many, many explanations but not many solutions. And the solutions that I found, did not work. In one of the three questions that you shared, there is one explanation that states that integers are passed by value when importing. Will investigate this further to see if we can have at least one clear solution to this problem. – Yster Jun 14 '17 at 23:15
  • @JohnY Tried the idea in the third question to use a list, but that didn't work either. Maybe I'm wrong, but it seems that none of the solutions you kindly supplied works. Thanks for trying. The answer by zwer seems to work though. – Yster Jun 15 '17 at 08:35
  • Ned Batchelder and Alex Martelli are extremely big names in the Python world. It is hard to imagine that they don't know what they're talking about. Have you taken the advice (from virtually every person who has answered or commented on your question as well as the questions I linked to as well as the many similar questions I didn't link to) to not use circular imports? Even zwer has told you not to do this. Do you have a compelling reason why you can't lift out the shared data into its own module? Once you do that, things become much simpler. – John Y Jun 15 '17 at 20:03
  • @JohnY Great idea to move the shared data to its own module! Sounds like a clean solution! You know, that was all I needed from the start: Just a (kind) pointer in the right direction! Thanks for that! – Yster Jun 15 '17 at 20:35

1 Answers1

1

When you execute your moduleA you're running it as a script - essentially a module with the name of __main__, not as a 'normal' module, and that's how it gets loaded. If you go and look through sys.modules as soon as you start it (before you import moduleB) you ain't gonna find your moduleA there but you'll find module __main__ (which will soon enough hold a variable with value 10).

Then when you import your moduleB, it imports moduleA - Python tries to find the loaded moduleA but there isn't any, so it tries to load it from the disk and voila! it gets loaded, but this time as moduleA. Python then looks for moduleB that is imported from moduleA, and since it's already there it doesn't make any fuss about it despite the cyclic dependency (and if you have something like that in your code - you're doing it wrong).

Anyway, since it's now loaded as moduleA, its if __name__ == "__main__": block evaluates to false so it doesn't cause any additional fuss. moduleB proceeds with its execution, but it also doesn't match its if __name__ == "__main__": block so it just gets loaded and sits there.

Now we're back in our __main__ representation of moduleA - its if __name__ == "__main__": block evaluates to true, so it calls its main() function, which in turn calls moduleB's main() function, which then changes the variable value but in moduleA, not in __main__. So, now when it gets back to __main__ and it tries to read its own variable value it gets the unchanged version of 10. But if you were to print out: print(getattr(sys.modules["moduleA"], "variable")) you'd see that your moduleB indeed changed the variable of moduleA.

If you really want to force the change in main, try having your moduleB like:

import sys

def main():
    moduleA = sys.modules["__main__"]  # this is the executing `moduleA` in your case
    print("From moduleB, variable =", moduleA.variable, " before change.")
    moduleA.variable = 20
    print("From moduleB, variable =", moduleA.variable, " after change.")

if __name__ == "__main__":
    main()

Which will, running your test case from moduleA, print out:

('From moduleA, variable =', 10, ' before change.')
('From moduleB, variable =', 10, ' before change.')
('From moduleB, variable =', 20, ' after change.')
('From moduleA, variable =', 20, ' after change.')

That's what's going on here, and the same is happening with your static vars example - you're consistently targeting the 'wrong' module form your moduleB. Either way, please do not do this in any sort of a production code or any situation where other developers might end up having to mess with your code - cyclic redundancies are the bane of all things nice.

zwer
  • 24,943
  • 3
  • 48
  • 66
  • Thank you so much for the explanation and possible solution zwer! I almost gave up hope on this thing. The problem is that I don't want to run moduleA again. For instance if I have a GUI for moduleA and a async API for moduleB. Then it will open up another GUI. Is it possible to do this without cyclic referencing? What is the best solution to this problem? Have to go to bed soon. Thanks again. – Yster Jun 14 '17 at 23:38
  • 1
    @Yster - well, don't load your `moduleA` again - instead of `import moduleA` in your `moduleB`, use `moduleA = sys.modules.get("__main__")` assuming that your `moduleA` is still the main running script. Explaining how to avoid cyclical references is, I'm afraid, a bit out of the scope of these comments - it essentially requires to rethink how you want to design your system and if there is no way (and 99% of the time there is) around two separate entities depending on each other for some reason, create a broker between them to serve as an intermediary. – zwer Jun 14 '17 at 23:44