3

I have a class with a long list of variables (at least a dozen). In the module I first import a constants file which contains some helper methods and all of the default constants.

from myConstants import *

class SomeClass(ParentClass):
    def __init__(var1=VAR1, ..., varN=VARN):
        super(SomeClass, self).init(var1, ...)
        self.varM = varM

PROBLEM: I would like to be able to specify a file when I initialize the class where the file contains a subset of the constants in myConstants.py. The variables not in the init file would be loaded from the defaults. Something like:

sc = SomeClass(initFile='/path/to/file')

My thought was to have something like

from myConstants import *
from ConfigParser import SafeConfigParser

class SomeClass(ParentClass):
    def __init__(var1=VAR1, ..., varN=VARN, initFile=''):
        if initFile: 
            parser = SafeConfigParser().read(initFile)
            var1 = parser('constants', 'var1') if parser('constants', 'var1') else VAR1
            var2 = parser('constants', 'var2') if parser('constants', 'var2') else VAR2
            ....
            varN = parser('constants', 'varN') if parser('constants', 'varN') else VARN
        else:
            var1 = VAR1
            ...
            varN = VARN

        super(SomeClass, self).init(var1, ...)
        self.varM = varM

QUESTION: Is this the best way to do this? If not, what is? Seems a little long winded. I'm also not married to ConfigParser.

6
  • 2
    tip: don't use "wildcard imports", like from foo import *, either explicitly do from foo import bar1, bar2, bar3, ... or alternatively import foo and do foo.bar1, foo.bar2 Commented Feb 10, 2016 at 14:52
  • 1
    So you should do the second of cowlicks's suggestions; just do import foo, then refer to them as foo.bar1 etc - no need to change anything when you add new ones. Commented Feb 10, 2016 at 14:56
  • 1
    The idea behind this is to keep your namespace small and clear. You get a lot of names and can not be sure where they are defined. Is this variable with the name length from the myConstants module or defined locally? With myConstants.length you definetely know. Commented Feb 10, 2016 at 15:01
  • 1
    Appreciate the feedback. Any thoughts on my actual question? :) Commented Feb 10, 2016 at 15:08
  • 1
    @sal there is a canonical style guide for writing python code known as pep8. The section on imports is here Commented Feb 10, 2016 at 15:22

3 Answers 3

2

I would use two separate constructors, one that takes a file and another that takes explicit arguments.

class SomeClass(ParentClass):
    @classmethod
    def from_file(cls, init_file):
        parser = SafeConfigParser()
        parser.read(init_file)
        var1 = parser.get('constants', 'var1') if parser.has_option('constants', 'var1') else VAR1
        # likewise var2, ..., varN
        # This results in a call to cls.__init__
        return cls(var1, var2, ..., varN)

    def __init__(var1=VAR1, ..., varN=VARN, initFile=''):            
        var1 = VAR1
        ...
        varN = VARN

        super(SomeClass, self).init(var1, ...)
        self.varM = varM

x = SomeClass(1, 2, 3)
y = SomeClass.from_file(config_file)
Sign up to request clarification or add additional context in comments.

Comments

1

Man, this ConfigParser clearly shows its age - it definettely should have a mapping interface - I had to poke a little around to get a way to get its values - but this is a more suitable code for you:

class SomeClass(ParentClass):
    options = {"var1": VAR1, "var2": VAR2, ...} 
    def __init__( initFile='', **kwargs):
        options = self.__class__.options.copy()
        if initFile: 
            parser = SafeConfigParser().read(initFile)
            for option_name in parser.options("constants"):
                if options_name in options:
                    options[option_name] = parser.get("constants", option_name)
        else:
            for key, value in kwargs:
                if key in options:
                    options[key] = value

        super(SomeClass, self).init(**options)
        self.varM = varM

Comments

0

You could try to use configparser object and modify it to suit your needs using a dictionary of default values:

default_values = {
    "var1": "42",
    "var2": "23",
}

conf_parser = configparser.ConfigParser()
conf_parser.read(config_file)

for var, default_value in default_values.keys():
    if v not in conf_parser["DEFAULT"]:
        conf_parser["DEFAULT"][var] = default_value

This should overwrite all missing values in the configparser. After that you can just pass along the configparser object and access all variables using config_parser["DEFAULT"]["var1"] and so on. This has the upside that all your variables are neatly bundled together and you only have one parameter for the constructor.

On the other hand this is a bit ugly and the config parser only knows how to work with strings, so maybe a dictionary would be better suited for this task.

I hope this helps.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.