Search code examples
python-3.xstringimportpython-modulemutable

mutable datatype similar to string in python


I have 2 files let say them a.py and b.py, b.py has a global variable let say test(data type is a string), and I’m importing test in a.py. Now, during runtime, a function in a.py calls a function of b.py, and that function changed the value of test in b.py but this change doesn’t show in a.py because the test is of string datatype and string is immutable.

I tried using the list, and it works, but I’m not feeling very good about using a list with one element in it. So, is there any mutable data type(similar to string) that I can use for this purpose?.

Code when the test is string.

b.py

test = "hello"
def changeTest():
    global test
    test = "hii"

a.py

from b import test,changeTest

def callFunctionInB():
    changeTest()
    
print(test)
callFunctionInB()
print(test)

output:

hello
hello

Code when the test is list.

b.py

test = ["hello"]
def changeTest():
    test[0] = "hii"

a.py

from b import test,changeTest

def callFunctionInB():
    changeTest()
    
print(test)
callFunctionInB()
print(test)

output:

['hello']
['hii']

Solution

  • Python import statements are extremely tricky, and your issue is because of poor usage of import statements.

    from ... import ... creates a copy of imported variable for reference. To validate this on your copy of python, try running:

    ### a.py
    import b
    from b import string_value, change_value
    
    print(string_value)
    change_value("new value")
    print(string_value)
    print("a.py ==> b.string_value = \t", id(b.string_value))
    print("a.py ==> string_value = \t", id(string_value))
    
    ### b.py
    string_value = "previous"
    
    def change_value(given_value):
        global string_value
        print("b.py ==> 'global' string_value \t", id(string_value))
        string_value = given_value
    

    Import statement in a.py is creating a copy of the variable for itself, so when you update the global variable of module b, it has no effect on the variable in a.

    To answer your question, there are a couple of possibilities:

    1. Use module level import. Don't use from ... import .... Example of this would be:
    ### a.py
    import b
    
    print(b.string_value)
    b.change_value("new value")
    print(b.string_value)
    
    ### b.py
    string_value = "previous"
    
    def change_value(given_value):
        global string_value
        string_value = given_value
    
    1. Use a third c.py configuration file or even better create a class for your use case with complete getters and setters. This is the best approach. Follow this approach for clean and maintainable code base. Lots of other SO answers have good documentation around this. Like Using global variables between files?

    2. If you absolutely have to use from ... import ..., you can use an ugly update syntax. Please avoid this at all costs. One of the above two things should be easily feasible. For your reference, sample code to do this would be:

    ### a.py
    from b import string_value, change_value
    
    print(string_value)
    change_value("new value")
    print(string_value)
    
    ### b.py
    # Please don't use this code except for understanding purpose
    import sys
    string_value = "previous"
    
    def change_value(given_value):
        sys.modules['__main__'].string_value = given_value