0

I have class Config() that loads configuration options. I create an instance of this class named cfg.
Then I have many classes A, B, C that have to read only members from cfg.

from mylib.config import *
from mylib.A import *
from mylib.B import *
from mylib.C import *

cfg = Config(filename)
a = A()
a.do_things()

and for instance in A constructor I have:

def __init__(self):
     self.name = cfg.name

problem is that I get at runtime "NameError: name 'cfg' is not defined" on line self.name = cfg.name

I first thought it's something that I import mylib.A, mylib.B and mylib.C BEFORE declaring cfg so I moved

cfg = Config(filename)

before importing mylib.A...B...C. But still same error.

Possible solutions:
1. pass object 'cfg' as argument, but I would have to change signature for all methods in all classes A, B, C and I feel it's not clean since it multiplies same argument accross all methods.
2. create private member _cfg for classes A, B, C. Better but nor very nice again, data is multiplied
3. put class definition + instance declaration in one file, and import it in mylib.A, mylib.B and mylib.C : AFAISI it's best solution, but still not my taste because I mix class definition and instance declaration, and I like to have only class definitions in class files
4. to avoid class definition and instanciation in same file, I can create a 'init.py'

from mylib.config import *
cfg = Config(filename)

and then import init in mylib.A, mylib.B and mylib.C... but I already have an init file, with log functions and since Config constructor uses those function, it creates circular import issues (init.py is imported in mylib.config.py class declaration file, and mylib.config is imported in init.py), to avoid it I should have init.py and init_config.py for instance.

Isn't there anything better/cleaner/simplier ?

comte
  • 3,092
  • 5
  • 25
  • 41

1 Answers1

0

How about, within mylib.config, you create an initialize function, which creates a global configuration object.

config_obj = None
#todo: pick a better name for this

class Config:
    def __init__(self, filename):
        pass
        #todo: definition goes here.

def initialize(filename):
    global config_obj
    config_obj= Config(filename)

Then, within your main file, you call that function before you do anything with modules A B or C.

from mylib.config import *
from mylib.A import *
from mylib.B import *
from mylib.C import *

initialize(filename)
a = A()
a.do_things()

Don't forget to import config in whatever modules need to refer to config_obj.

#mylib.a
import config

class Whatever:
    def __init__(self):
         self.name = config.cfg_obj.name

Note that you shouldn't replace import config with from config import *, because then A's copy of cfg_obj won't get properly updated when the main file calls initialize.


If you don't like your class modules having functions or global variables, you can make initialize a static method of the Config class, which returns the singleton instance of Config.

#singleton Config class. Only one instance of it should ever exist.
#Users, please don't call Config(filename). use get_instance() instead.
class Config:
    instance = None
    def __init__(self, filename):
        pass
        #definition goes here...

    @staticmethod
    def initialize(filename):
        if Config.instance is not None:
            msg = "singleton Config has already been initialized"
            raise Exception(msg)
        #this is the only place Config(filename) should ever be called.
        Config.instance = Config(filename)
        return Config.instance

    @staticmethod
    def get_instance():
        if Config.instance is None: 
            msg = "can't get instance because it hasn't been initialized yet"
            raise Exception(msg)
        return Config.instance

(There may be better ways to make a singleton in Python. I just went with a simple approach for illustrative purposes.)

Then you initialize in the main file:

from mylib.config import *
from mylib.A import *
from mylib.B import *
from mylib.C import *

Config.initialize(filename)
a = A()
a.do_things()

And get the instance in the modules:

#mylib.a
import config

class Whatever:
    def __init__(self):
         self.name = config.Config.get_instance().name

(With this approach, I think you're able to from config import * in the modules if you like.)

Community
  • 1
  • 1
Kevin
  • 74,910
  • 12
  • 133
  • 166
  • your proposal looks like my third solution with encapsulation and 'global' use, two things not necessary IMHO (I may be wrong)... i try to avoid mixing declaration and definition in mylib.config. – comte Nov 19 '14 at 14:31
  • @comte, ok, you can put everything in classes if you use the singleton pattern. Edited. – Kevin Nov 19 '14 at 14:47