I started to write python programs for some time, but I am not so good at it, at present I meet a problem, it seems very strange, at least for me.
Assume we have two source files, called A.py and B.py, their contents are as below:
A.py
import B
__global__ = 'not set yet'
class Data:
__var__ = 'not set yet'
def init():
global __global__
__global__ = 'value set for global'
Data.__var__ = 'value set for class var'
if __name__ == '__main__':
init()
t = B.Test()
t.display()
print '============================'
print 'global(in A): ' + __global__
print 'class var(in A): ' + Data.__var__
B.py
import A
class Test:
def __init__(self):
self.glo = A.__global__
self.var = A.Data.__var__
def display(self):
print 'global: ' + self.glo
print 'class var: ' + self.var
print '============================'
print 'global(in B): ' + A.__global__
print 'class var(in B): ' + A.Data.__var__
then I ran python A.py, the output is as following:
global: not set yet
class var: not set yet
============================
global(in B): not set yet
class var(in B): not set yet
============================
global(in A): value set for global
class var(in A): value set for class var
In my opinion, the first 4 outputs should coincide with the last 2 outputs, but the fact is not, seems the value is set during class and method definition and cannot be changed. It is very different from many other languages, such as Java. So, could any one help explain this, or paste me some links to help understand it? And is there any workaround to solve this?
Thanks in advance,
Kelvin
==============edit==============
Thanks @icktoofay, @hop and @RocketDonkey, after I tried the code below, I do find the root cause:
import sys
import A
import __main__
__global__ = 'not set yet'
class Data:
__var__ = 'not set yet'
def init():
global __global__
__global__ = 'value set for global'
Data.__var__ = 'value set for class var'
if __name__ == '__main__':
init()
for key in sys.modules.keys():
if key in ['__main__', 'A']:
print key + ' : ' + sys.modules[key].__file__
print '===================='
print 'A: ' + A.__global__
print 'A: ' + A.Data.__var__
print '===================='
print __main__.__global__
print __main__.Data.__var__
the output is:
__main__ : A.py
A : D:\test\python\A.py
====================
A: not set yet
A: not set yet
====================
value set for global
value set for class var
It is because file A.py was imported twice, one is named __main__ and another is named A, the values were changed in module __main__, but for module B, the value is got from module A, so the value is not changed.
I do need to dive deeper for module importing of python. 😀
The circular imports (
AimportsB,BimportsA) in combination with one of the modules being the main script is the problem.How Python imports modules
When you ask Python to import a module, it first looks through
sys.modulesto see if a module by that name is already there. If so, it just uses that module. If there is no module by that name, it makes a new module object, places it insys.modules, and runs the appropriate Python code in that module.Main scripts
There’s a popular idiom that’s used in Python scripts: the ubiquitous
When Python wants to run the main script, it imports that module with the name
__main__.That’s perfectly fine when your main script just does stuff with other modules, but when other modules want to do things to the main module again, you run into trouble: The script importing the main module again is probably importing it by a normal name (like in this case,
A). Unfortunately, the already-loaded module is not namedA; it’s named__main__.Solutions
A very simple solution would simply be to remove the other modules’ dependencies on the main module. Then you will not encounter this unintuitive behavior. One way you might do this is have the main script just be a stub that calls out to another module’s
mainfunction or something.A different solution would be to have the main script alter
sys.modulesmanually to put itself under another name. This is a little hacky, but it works:Now you know.