Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Accessing a class instance in a library from two separate scripts in a project

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.

like image 915
Dan Springer Avatar asked Oct 17 '22 22:10

Dan Springer


2 Answers

There are two things you need to understand here:

1. Module caching

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.

2. References

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!
like image 96
Ashwini Chaudhary Avatar answered Oct 29 '22 16:10

Ashwini Chaudhary


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 is sys.modules. This mapping serves as a cache of all modules that have been previously imported, including the intermediate paths. So if foo.bar.baz was previously imported, sys.modules will contain entries for foo, foo.bar, and foo.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.

like image 23
Patrick Haugh Avatar answered Oct 29 '22 18:10

Patrick Haugh