I have a class with an attribute that is a dictionary of dataframes, where each dataframe is the input data grouped at different levels of temporal aggregation.

I need to add an option to this class so that when the object is instantiated, the dictionary contains a slightly different grouping of dataframes.

Would it be acceptable to include a flag as part of the __init__ function that provides the option of what type of dictionary to build when the object is instantiated?

So, something like this:

class Foo():
   def __init__(self, data, agg_type):
      if agg_type=='type1':
         df_hour = data.groupby(['year', 'month', 'day', 'hour'])
         df_week = data.groupby(['year', 'week'])
         self.grouped_data = {
              'hour': df_hour,
              'week': df_week
      if agg_type=='type2':
         df_hour = data.groupby('hour')
         df_week = data.groupby('week')
         self.grouped_data = {
              'hour': df_hour,
              'week': df_week}

foo = Foo(input_df, agg_type=='type1')

If not, then I suppose I can have a parent class and two children classes that have different __init__ attributes. The question then becomes what do I put in the __init__ of the parent class?

  • 2
    To the down-voters and close-voters: the OP is not asking for a code solution. This is a conceptual question about when you add a constructor parameter or split the class into multiple sub classes. This should be perfectly on-topic for this site. Oct 7, 2021 at 22:29

1 Answer 1


Introducing a class hierarchy should also introduce some form of abstraction. If the change is a data change only which does not require the consumers of these classes to change their behavior, then I would advocate for a constructor parameter instead of a class hierarchy:

# old code   |  New code
foo = Foo()  |  foo = Foo("x") # new constructor arg is passed
foo.bar()    |  foo.bar()

If the introduction of that constructor parameter would cause the consumers of this object to make a decision:

# old code   |  New code
foo = Foo()  |  foo = Foo("x")
foo.bar()    |  if something == "x"
             |    foo.bar()
             |  else
             |    foo.baz()

then I would create a class hierarchy and a proper abstraction, so existing code does not need to be refactored. This would be viewed as specialization of behavior, and object-orientation is well suited to handle this.

  • I added a specific example to my original question. In this instance, existing code would need to be refactored by adding the agg_type argument, but not a big deal. If that's not correct, please provide an example of how a class hierarchy would look in my context. Oct 7, 2021 at 20:55
  • 1
    @matsuo_basho: my answer still stands. Sure, you need to refactor the code that initializes the object, but after that, what changes? If nothing changes, just keep it simple and add the new parameter. Oct 7, 2021 at 21:00

Your Answer

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

Not the answer you're looking for? Browse other questions tagged or ask your own question.