1

Problem

Many people say their variable-sharing problems are resolved with the approach provided here and here, but neither of them works in my use case, where all configurations have to be written in a class like following.

# config.py
class Config(object):
    var = None

The structure of my project looks like

├── config.py
├── main.py
├── subfolder
|   ├── change_config.py

In this project, main.py will invoke change_config.py and they both have access to variables defined in config.py. Importantly, change_config.py will modify the variables (aka., var) whose value is only known during runtime.

But it is not clear how I should share the instantiated Config() (aka. opt) across main.py and change_config.py. I tried following but no luck. The issue is

  • If I instantiated another Config() in change_config.py, the one in main.py will be wiped out.
  • If I do not do the first one, then name opt would not be resolved in change_config.py
# main.py
import config
from subfolder import change_config

opt = config.Config()
print(opt.var)
change_config.change_config()
print(opt.var)
# change_config.py
import config

def change_config():
    opt.var = 10

More information

If I run main.py, then I will have

NameError: name 'opt' is not defined

which is expected since opt is never declared in change_config.py.

If I change change_config.py into

# change_config.py
import config

opt = config.Config()
def change_config():
    opt.var = 10

where I declared another opt. There is no error but returned

None
None

which is also expected since opt declared in main.py is wiped by the one in change_config.py. But the expected output should be

None 
10

So the question is how to share opt in main.py with change_config.py

Mr.Robot
  • 349
  • 1
  • 16
  • 1
    Can you share the traceback? I wouldn't expect a `var` does not exist error. Also, `subfolder.change_config` should not be callable since it's a module – Iain Shelvington Nov 15 '19 at 03:37
  • @IainShelvington Thank you for your help! The code turns out to be working fine. But please have a look at the "edit` part for additional issue for my use case. – Mr.Robot Nov 15 '19 at 05:06
  • Instead of making an edit, could you rewrite your question? The fact that you initially had the import wrong is irrelevant and confusing to the actual issue. Not that finding the solution on your own wasn't commendable. – Mad Physicist Nov 15 '19 at 05:22
  • @MadPhysicist Thank you for your suggestion! Already done that. – Mr.Robot Nov 15 '19 at 05:43
  • What error do you get? Please post a traceback. Hint, where does `opt` come from in `change_config`? – Mad Physicist Nov 15 '19 at 05:44
  • @MadPhysicist This is exactly the difficulty I have. I do not know how to pass `opt` into `change_config` if I could only instantiate it in `main.py`. – Mr.Robot Nov 15 '19 at 05:50
  • Go ahead and post the traceback for completeness. You're really making this more complicated than it needs to be, and I'll be happy to draft you an answer proving it :) – Mad Physicist Nov 15 '19 at 05:52
  • @MadPhysicist Please see "more information" part. – Mr.Robot Nov 15 '19 at 06:00
  • @Mr.Robot why don't you create a single config.py file and declare your variables in there and then just import config.py file in all modules of your application, the module then becomes available as a global name by doing so any changes to the module object get reflected everywhere – suraj deshmukh Nov 15 '19 at 06:07
  • This could be a good case to use the singleton pattern https://python-3-patterns-idioms-test.readthedocs.io/en/latest/Singleton.html you can make Config a singleton which means that there will only be one instance of it. – Will Munn Nov 15 '19 at 06:45

2 Answers2

0

Let's start by looking at what the differences between Config.var and Config().var are in python terms. Like everything else in Python, a class is an object. A class has a namespace, so Config.var is a variable in that namespace.

When you instantiate Config, say with opt = Config(), opt is now an object, also with a namespace. At first, when you request the value opt.var, it returns Config.var because of how inheritance works in general: if an object does not have an attribute, you look in the class, and then in the parent class, etc. But when you assign opt.var = 10, you are assigning into the namespace of the instance. Now, when you request the value of opt.var, it does not look at Config.var any more, because opt has its own var attribute.

Now keep in mind that opt in main and opt in change_config are completely different instances that do not affect each other except that they share the same class. So when you set change_config.opt.var = 10, requesting main.opt.var still returns None, because the actual attribute main.opt.var does not exist as such: it is returning Config.var from the class namespace.

So now you have a couple of options available to you.

  1. The first is not to bother instantiating anything at all. You can keep your class variable config.Config.var, and just update in the class namespace. The code would look like this:

    main.py

    from config import Config
    from subfolder.change_config import change_config
    
    print(Config.var)
    change_config()
    print(Config.var)
    

    config.py

    class Config:
        var = None
    

    subfolder/change_config.py

    from config import Config
    
    def change_config():
        Config.var = 10
    

    Aside from changing your imports to relative imports and removing the explicit inheritance from object, your code is only modified to use Config as a namespace, without instantiating it.

  2. A second approach would be to instantiate Config, but put the reference in a place that can be accessed by everyone that needs it. At that point, config.py should really be maintaining the shared reference, since that is what stores the actual configuration. I would also recommend removing the class variable Config.var entirely, to avoid exactly the type of confusion you are having now:

    main.py

    from config import opt
    from subfolder.change_config import change_config
    
    print(opt.var)
    change_config()
    print(opt.var)
    

    config.py

    class Config:
        def __init__(self):
            self.var = None
    
    opt = Config()
    

    subfolder/change_config.py

    from config import opt
    
    def change_config():
        opt.var = 10
    

    You could commit to making your configuration an instance of Config even deeper by adding del Config to the end of config.py. This will make it much more difficult to create another, conflicting instance of the class, since it will not be readily available through the module. The only reference to Config will be through the inheritance hierarchy of opt at that point.

Mad Physicist
  • 107,652
  • 25
  • 181
  • 264
-1

you can change your application like below, this will help you

in config.py

x = 0

in change_config.py

import config

def change_config():
    config.x = 10 #change the global config variable

and in main.py

import config
from subfolder import change_config

print(config.x)
change_config.change_config()
print(config.x)

you will get output like below after you run main.py

0
10

EDIT

using singleton class

in config.py

class Config(object):
    var = None
    def __new__(cls):
        if not hasattr(cls, 'instance'):
            cls.instance = super(Config, cls).__new__(cls)
        return cls.instance

in main.py

from config import Config
from subfolder import change_config

opt = Config()
print('memory address :%s'%id(opt))
print(opt.var)
change_config.change_config()
print(opt.var)

in change_config.py

from config import Config

opt = Config()
print('memory address :%s'%id(opt))
def change_config():
    opt.var = 10

You will get output like below

memory address :140185708587536
memory address :140185708587536
None
10
suraj deshmukh
  • 188
  • 1
  • 10
  • Thank you for your help. I have tried this one and this indeed worked. But according to my use case, all configurations **have to** be written in the class `Config()` and this is the difficulty I am facing. – Mr.Robot Nov 15 '19 at 06:26
  • 1
    Singleton class seems possible solution for your problem. Create singleton Config class and import it wherever you require, in this way you can access/change the configurations without creating multiple objects of Config class – suraj deshmukh Nov 15 '19 at 06:52