1

Consider a structure consisting out of a container instance with different class instances instantiated inside which should be configurable via a config file e.g. in the form of a dictionary. What is the pythonic way to assign all the parameters?

I can think of the following 2 options. Which is more pythonic, and why? What general approach is most pythonic, and why?

Option 1a, my current approach. Each contained instance has kwargs:

# hundred parameters
params = {'A_param_1':0,'B_param_1':0,...}

class Container():
    def __init__(self,params):

    self.InstanceA = self.ClassA(**params)
    self.InstanceB = self.ClassB(**params)
    ...
    self.InstanceF = self.ClassF(**params)

class ClassA():
     def __init__(self,
                  A_param_1=0,
                  ...
                  A_param_16=0.123,
                  **kwargs)
     # set param values to self.
     self._A_param_1 = A_param_1

Option 1b, same but more verbose without **params:

class Container():
    def __init__(self,params):

    self.InstanceA = self.ClassA(all written out.. )
    self.InstanceB = self.ClassB(all written out.. )
    ...
    self.InstanceF = self.ClassF(all written out.. )

Option 2 - colleague's approach. Each contained instance does without kwargs:

# hundred parameters
params = {'A_param_1':0,'B_param_1':0,...}

class Container():
    def __init__(self,params):

    self.InstanceA = self.ClassA(params)
    self.InstanceB = self.ClassB(params)
    ...
    self.InstanceF = self.ClassF(params)

class ClassA():
     def __init__(self,
                  params
                  )
     # set param values to self.
     self._A_param_1 = self.params['A_param_1']

Pros option 1a: each constructor has default valued kwargs making the nature of these kwargs explicit, literally.

Cons option 1a: one doesnt know whether default values are used or the ones given via **params. **params throws in all parameters, not just the ones ment for the particular instance.

Pros option 2: each instance grabs what it needs in an explicit manner with a key-error if the specific parameter isnt provided.

Cons option 2: The typical values of the parameters would have to be made explicit in the docstring or comments.

Option 1b seems to combine a bit the flavours of option 1a and option 2 with the added difficulty of having to explicitly unpack **params in the constructor call.

To repeat my question: In general what is the most pythonic way to approach this?

PS: I hope I make myself understood and use somewhat the right terms to express myself. If not please let me know.

balletpiraat
  • 206
  • 1
  • 11
  • Possible duplicate of https://stackoverflow.com/questions/1389180/python-automatically-initialize-instance-variables – cs95 Oct 04 '17 at 15:12

1 Answers1

0

I don't like passing all the parameters to each constructor. I would use a configuration of nested dictionaries like this, possibly stored in a JSON file:

# hundred parameters
params = {'A': {'A_param_1': 0, ..., 'A_param_16': 1},
          'B': {'B_param_1': 0, ...},
          ...
          'F': {'F_param_1': 0, ...}}

class Container():
    def __init__(self, params):
        self.InstanceA = ClassA(**params['A'])
        self.InstanceB = ClassB(**params['B'])
        ...
        self.InstanceF = ClassF(**params['C'])

class ClassA:
    def __init__(self,
                 A_param_1=0,
                 ...
                 A_param_16=0.123):
        # set param values to self.
        self._A_param_1 = A_param_1

I would also probably automate the container initializer by calculating class names and instance names like this:

class ClassF:
    def __init__(self, foo):
        self.foo = foo

def main():
    c = 'F'
    n = 23
    cls = globals()['Class' + c]
    o = cls(n)
    print(o.foo)  # => 23

main()

You could use setattr() for self.InstanceA.

Don Kirkby
  • 53,582
  • 27
  • 205
  • 286