0

I have a situation where a base class method (the __init__ method in Python) does a lot of work. The derived classes override __init__ to simply set an object variable and then delegate to the base class __init__. This works fine but now I have a situation.

I have some code that fits perfectly in the base class __init__ which all the other derived classes except one (call it A) need to use. Imagine if the base class __init__ was something like this.

def __init__(self):
   Action 1...
   Action 2...

Now, all the derived classes simply delegate directly to this one. However, A should have only Action 2 in its __init__. Now the sitation is ugly. I can split Action 1 and Action 2 into two separate functions and have the base class simply call Action2. Then most classes will have their init something like this.

def __init__(self):
   self.Action1()
   Other init code...
   super().__init__()

and for class A, it will be

def __init__(self):
   Some init code....
   super().__init__()

But you can see that there's a piece of code (the call to self.Action1 that) I have to repeat in most of the derived classes. This is bad. I'm not sure how to code this up elegantly with minimum repeated code and need some advice on how to do it.

Noufal Ibrahim
  • 71,383
  • 13
  • 135
  • 169
  • 1
    the typical answer is _maybe A is not a child class really_. Consider putting A as a parent (higher in the hierarchy, instead of lower) – Aganju Jul 04 '16 at 18:34
  • In ```A``` can you *undo* ```Action1``` after a call to ```super().__init__()```? – wwii Jul 04 '16 at 18:47
  • @Aganju A is a child. The base class contains a lot of common code. The child classes implement a few methods which makes them fit into the larger framework. – Noufal Ibrahim Jul 04 '16 at 18:48
  • @wwii Not exactly. `Action1` is something like a verison check. If it fails, an exception is raised and nothing can proceed. – Noufal Ibrahim Jul 04 '16 at 18:50
  • 1
    Class attribute that delineates which actions to perform and logic in ```__init__``` to perform them - then ```A``` overrides that attribute? – wwii Jul 04 '16 at 18:54
  • 1
    @NoufalIbrahim, i understood that A is a child. I am saying _maybe it should not be a child_, or maybe something should be above your parent where A is derived from. – Aganju Jul 04 '16 at 18:55
  • 2
    Have you considered a composition solution instead? Perhaps there should be an object that does `Action1` which can be a member of the other children (not `A`). The other action is accomplished by inheritance. – Rick Jul 04 '16 at 19:02
  • @wwii that's what I've done now. Is it the "standard" way of dealing with this? – Noufal Ibrahim Jul 05 '16 at 08:19
  • @RickTeachey Can you elaborate a little in an answer? That sounds interesting. – Noufal Ibrahim Jul 05 '16 at 08:20
  • @NoufalIbrahim - to be candid I have approximately zero practice with classes, I rarely use them but that solution came to mind. – wwii Jul 05 '16 at 16:34

3 Answers3

3

The clearest solution would be to use a default argument:

class Base:
    def __init__(self, *, doAction1=True):
       if doAction1:
           Action 1...
       Action 2...

Then in class A you can have:

def __init__(self):
   Some init code....
   super().__init__(doAction1=False)
ecatmur
  • 152,476
  • 27
  • 293
  • 366
0

Class attribute that can be overridden - similar to default argument in __init__. (Python v2.7 class definition).

class Foo(object):
   action1 = True
   action2 = True
   def __init__(self):
      if self.action1:
         print('action1')
      if self.action2:
         print('action2')

class Bar(Foo):
   def __init__(self):
      super(Bar, self).__init__()
      print('Bar')

class Baz(Foo):
   action2 = False
   def __init__(self):
      super(Baz, self).__init__()
      print('Baz')
wwii
  • 23,232
  • 7
  • 37
  • 77
0

When making design decisions, it is often worth considering whether inheritance is the right way to go. Think deeply about whether your really do need inheritance, or whether it's just going to make things complicated.

If you were to decide that inheritance is not actually needed (the current issue you are asking about may- or may not- be a sign that this is the case), before resorting to inheritance I suggest you explore solving your problem using something like this:

def Action1(obj):
    <do Action 1>

def Action2(obj):
    <do Action 2>

def Action1and2(obj):
    Action1(obj)
    Action2(obj)

class A:
    def __init__(self):
        Action2(self)

class Other:
    def __init__(self):
        Action1and2(self)

Note that for Other objects, you still only have 1 line of code, and you are avoiding inheritance altogether.

If you were to decide that inheritance IS needed- ie, the parent objects you are creating do more than just setting things up with some actions (they provide additional functionality or access to data)- you could attempt to solve your problem using composition instead, like this:

class Action1Doer:
    def __init__(self,obj):
        <do Action 1>

class Action2Doer:
    def __init__(self,obj):
        <do Action 2>

class Action1and2Doer:
    def __init__(self,obj):
        self.a1 = Action1Doer(obj)
        self.a2 = Action2Doer(obj)

class A:
    def __init__(self):
        self.thing2 = Action2Doer(self)

class Other:
    def __init__(self):
        self.thing1and2 = Action1and2Doer(self)

Note that, again, you have only a single line of code in each of the initialization methods. By the way, going about things in this manner is certainly nothing new in the coding world.

You could also mix things together:

def Action1(obj):
    <do Action 1>

class Action2Doer:
    def __init__(self,obj):
        <do Action 2>

class Action1and2Doer:
    def __init__(self,obj):
        Action1(obj)
        self.a2 = Action2Doer(obj)

class A:
    def __init__(self):
        self.thing2 = Action2Doer(self)

class Other:
    def __init__(self):
        self.thing2 = Action1and2Doer(self).a2
Community
  • 1
  • 1
Rick
  • 43,029
  • 15
  • 76
  • 119
  • I suppose this is possible but "Action1and2Doer" sounds rather contrived. No? – Noufal Ibrahim Jul 06 '16 at 05:52
  • That's essentially what the base class you are already using is, right? I'm just giving it a descriptive name to make the explanation clear. – Rick Jul 06 '16 at 11:17