I searched all over and could not come up with a reasonable search query to produce helpful results. I'll try to explain this with a simple example (that is tested).
Suppose I have some small custom Python library that contains just the following private class and public instance of it:
#!/usr/bin/env python
class _MyClass(object):
def __init__(self):
self.val = "Default"
my_instance = _MyClass()
Now, I also have two other python files ('file_a' and 'file_b') that will end up importing this instance from my library as seen below.
The full code in 'file_a':
#!/usr/bin/env python
from my_lib import my_instance
my_instance.val = "File A was here!"
import file_b
file_b.check_val()
The full code in 'file_b':
#!/usr/bin/env python
from my_lib import my_instance
def check_val():
print "From 'file_b', my_instance.val is: {}".format(my_instance.val)
The resulting output, if I only execute 'file_a' within a directory that also contains 'file_b' and 'my_lib', is this:
From 'file_b', my_instance.val is: File A was here!
Can someone explain to me how 'file_b' is able to access the same exact instance as 'file_a' in my example? Does this have to do with how the value being set in 'file_a' is global?
By the way, I do know I can just make 'MyClass' public again and instantiate it whenever a unique instance is needed in either 'file_a' or 'file_b', but the main reason I am posting this question is to wrap my head around this specific concept.
There are two things you need to understand here:
Python caches module imports to improve performance, this happens even when you do from foo import bar
. The module object gets stored in sys.modules
.
Hence, in your case both file_a
and file_b
are accessing same module object my_lib
and same instance my_instance
.
In Python variable assignment is basically adding a new reference to the same object, this is true for imports as well.
from my_lib import my_instance
is basically
import my_lib
my_instance = my_lib.my_instance
del my_lib
Now as we modify this instance in file_a
, we have basically modified the instance in my_lib
, and file_b
will also see this change.
You can modify file_a
and file_b
to verify this.
file_a
:
#!/usr/bin/env python
from my_lib import my_instance
my_instance.val = "File A was here!"
print "Inside file_a"
import sys
print id(sys.modules['my_lib']), sys.modules['my_lib'].my_instance, my_instance
import file_b
file_b.check_val()
file_b
:
#!/usr/bin/env python
from my_lib import my_instance
print "Inside file_b"
import sys
print id(sys.modules['my_lib']), sys.modules['my_lib'].my_instance, my_instance
def check_val():
print "From 'file_b', my_instance.val is: {}".format(my_instance.val)
Output(check the object IDs):
>>> %run file_a.py
Inside file_a
4396461816 <my_lib._MyClass object at 0x106158ad0> <my_lib._MyClass object at 0x106158ad0>
Inside file_b
4396461816 <my_lib._MyClass object at 0x106158ad0> <my_lib._MyClass object at 0x106158ad0>
From 'file_b', my_instance.val is: File A was here!
If you import my_lib
in each file and then print(id(my_lib))
, you'll see that the only a single module object is created.
Reading The import
system from the python docs may help you understand what's going on here. Of particular relevance is the section on The module cache. In part:
The first place checked during
import
search issys.modules
. This mapping serves as a cache of all modules that have been previously imported, including the intermediate paths. So iffoo.bar.baz
was previously imported,sys.modules
will contain entries forfoo
,foo.bar
, andfoo.bar.baz
. Each key will have as its value the corresponding module object.During import, the module name is looked up in
sys.modules
and if present, the associated value is the module satisfying the import, and the process completes.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With