'How to use and update same variable across multiple modules in python independently?

I have 3 files and I want to do something like below

[1] conf.py

var = 10  # Intialized with 10 (start)

[2] file_1.py

import conf
print(conf.var)   # Prints 10
conf.var = 1000   # Updated to 1000

[3] file_2.py

import conf
print(conf.var)   # Prints 1000
conf.var = 9999   # Updated to 9999

I want something like this. Assume that the files, file_1 and file_2 will be running and will stay in memory unless pressed CTRL+C. How can I change var in other 2 files and persist the value for it? Final value for var should be 9999 if we were to use it in file_3 like other 2 files. [It should print 9999] and so on.

Execution order file_1.py -> file_2.py.

Help me with identifying some way to do it or some module/package that can handle this.

Thanks! :)



Solution 1:[1]

I worked on this problem. I will suggest to use shared memory cache providers like Redis or memcached. Both start a server instance that we need to connect and use it like a key-value store. Note than values should be of type str or bytes.

Memcached

Install memcached
sudo apt install memcached
Start Server with default settings

You need to start the server before using it or you can set it as a service in linux

sudo service memcached start
Install python memcached client
pip install pymemcache
Sample Code
from pymemcache.client import base

client = base.Client(('localhost', 11211))
client.set('some_key', 'stored value')

client.get('some_key')  # Returns "stored value"

Redis

The process is exact same for redis

Install redis
sudo apt install redis
Start Server with default settings

You need to start the server before using it or you can set it as a service in linux

sudo service redis start
Install python redis client
pip install redis
Sample Code
import redis

client = redis.Redis()  # Default settings/credentials
client.set('some_key', 'stored value')

client.get('some_key')  # Returns "stored value"

Usage

  • Redis gives more safety and fault tolerance.
  • Both has some memory limits but is configurable. If your data is more in size, use files or db suitable to your problem.
  • Both are easy to configure

Solution 2:[2]

Couldn't you use a class and never initialize an object?

class Conf:
    var = 10

You would then update Conf with:

from conf import Conf

...

Conf.var = ...

Just never use Conf()..., as this creates an object instance.

Solution 3:[3]

Consider this approach:

class Lookup:
    # For allowing storing and looking up variables using variable sets of key pairs.
    # Each key pair is a key-value pair tuple.
    # The entire set of key pair tuples uniquely stores and retreives a variable value.
    # frozenset is used to achieve the benifit of a set while ensuring immutability, so it can be hashed for use as a dictionary key.
    lookupDictionary = {}
    def put(*, keyPairs, value):
        Lookup.lookupDictionary[frozenset(keyPairs)] = value
    def get(*, keyPairs, default, matchWildcard = False): # No substring searching. only '*' can be used for searching for an entire string
        if matchWildcard and True in [bool(x) for x in [(y[1] == '*') for y in keyPairs]]:
            # If we are here, we need to match using wildcard. 
            valuedKeyPairs = set([y for y in keyPairs if not y[1] == '*'])  # Separating value pairs from wildcard pairs
            starKeyPairs = set(keyPairs) - valuedKeyPairs
            starKeyPairNames = [x[0] for x in starKeyPairs]
            return [Lookup.lookupDictionary[i] for i in Lookup.lookupDictionary.keys() if set(valuedKeyPairs).issubset(i) and sorted(starKeyPairNames) == sorted([x[0] for x in i-valuedKeyPairs])]
        return Lookup.lookupDictionary.get(frozenset(keyPairs), default)
    def getAllValues(*, keyPairs):
        # Returrns all qualifying values to part or all of the key pairs
        return [Lookup.lookupDictionary[i] for i in set(Lookup.lookupDictionary.keys()) if set(keyPairs).issubset(i)]
class utils:
    def setCacheEntry(*, zipFileName, functionName, value):
        Lookup.put(keyPairs = [('__zipFileName__',zipFileName),('__functionName__',functionName)],value=value)

    def getCacheEntry(*, zipFileName, functionName, default):
        return Lookup.get(keyPairs = [('__zipFileName__',zipFileName),('__functionName__',functionName)],default=default)
from Lookup import utils
from Lookup import Lookup
utils.setCacheEntry(zipFileName='FileName.zip',functionName='some_func',value='some_value')
utils.getCacheEntry(zipFileName='FileName.zip',functionName='some_func', default=1)

Output: 'some_value'

This is a more simple case:

from Lookup import Lookup

def setParamValue(*, paramName, value):
    Lookup.put(keyPairs = [('__paramName__',paramName),],value=value)
    
def getParamValue(*, paramName, default):
    return Lookup.get(keyPairs = [('__paramName__',paramName),],default=default, matchWildcard=True)
    
setParamValue(paramName='name', value='John')
setParamValue(paramName='age', value=23)
getParamValue(paramName='name', default='Unknown')

Output: John

getParamValue(paramName='age', default=-1)

Output: 23

getParamValue(paramName='*', default='Unknown')

Output: ['John', 23]

If you need to communicate variables/objects between different programs, the above does not work. To do it you may use pickle to dump and load objects across the OS: I have not tested it to see if it does deep or shallow dumping of objects, but probably the later.

import pickle

# Dumping

with open('object_pickle','wb') as f:
    pickle.dump('some_value', f)    

# Loading
    
with open('object_pickle','rb') as f2:
    var = pickle.load(f2)

print(var)

Output: 'some_value'

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 Pratik Ghodke
Solution 2 Nicholas Barrow
Solution 3