3

I'm new to Python and trying to understand the best "Pythonic" practices for OO and inheritance.

Here's a simplified version of something I'm trying to do: Let's say I have a base class A that has an initialization method, which, in turn, calls another method that sets some internal parameters -- I'd like to have the latter method accessible to clients as an independent function/service that can be reused after initialization:

class A(object):

    def __init__(self):

        print "Entered A's __init__"

        #Initialization specific to A:
        print "Calling A's set_params"
        self.set_params()

    def set_params(self):

        print "Entered A's set_params"      

        #Parameter setting specific to A:
        #...

Then as expected, the output of A() prints the following:

Entered A's __init__
Calling A's set_params
Entered A's set_params

So far there's no problem. Next, I add a sub class B that inherits from A but has more specific tasks going on in addition to the inherited ones:

class B(A):

    def __init__(self):

        print "Entered B's __init__"

        #Inheriting initialization from A:
        print "Calling A's __init__"
        super(B, self).__init__()

        #More initialization specific to B:
        #...

    def set_params(self):

        print "Entered B's set_params"

        #Inheriting parameter setting from A:
        print "Calling A's set_params"
        super(B, self).set_params()

        #More parameter setting specific to B:
        #...

The problem is that I expect the initializer A.__init__ called by B.__init__ to operate completely independently of how I'm overriding the functions of B, so that overriding the functions of B does not change the behavior of A when instantiating B. In this case, I need A.__init__ to call A.set_params as before rather than calling B.set_params, and disregard the fact that the latter is being overridden.

In other words, in this case I obtain the following output after running B():

Entered B's __init__
Calling A's __init__
Entered A's __init__
Calling A's set_params
Entered B's set_params
Calling A's set_params
Entered A's set_params

And the question is, what should I do to get this instead after running B()?

Entered B's __init__
Calling A's __init__
Entered A's __init__
Calling A's set_params
Entered A's set_params

The problem would disappear if I simply got rid of A.set_params and put its code content inside A.__init__, but as I mentioned, I'd like it to be separate and accessible by client code independently.

I understand that it has something to do with the fact that functions are bound to instances in this case rather than to classes, and I have tried static methods, class methods, and abstract methods as well, but I could not figure out the correct combination to solve this problem.

Some insight would be greatly appreciated! :-)

5
  • 1
    You may want to consider calling a different set_params function in class A, as then it wouldn't be overwritten by class B Commented Aug 6, 2016 at 4:48
  • 1
    Ummm. At a loss differentiating between what you want done vs what your wishes are about the call stack. Could you maybe set some variables in your inits and setups and then indicate your desired resulting state vs what you got? Commented Aug 6, 2016 at 15:26
  • @solarnz: Thanks for the comment. That sounds like a viable solution. Commented Aug 8, 2016 at 19:40
  • @JLPeyret: Thanks for the comment. To answer your question, the A.set_params method initializes basic parameters for an instance of A which appear also in B but in a more constrained form. By that I mean every time you changed those parameters, some other parameters had to be updated accordingly, which I accomplished by overriding B.set_params to reflect that. However, I didn't want to set those additional parameters when initializing via B.__init_ which is why I wanted A.set_params to be called rather than B.set_params. Commented Aug 8, 2016 at 21:29
  • @JLPeyret: After thinking about it a bit more, I realized it was a bad design in the first place so I changed it to eliminate the need for all of this. Thanks again! Commented Aug 8, 2016 at 21:29

1 Answer 1

3

Yeah, designing for inheritance is tricky like that. People don't put as much thought as they really should into what overridden methods do or don't affect, and they don't do enough to document how overriding methods affects other methods.

If you want to bypass overrides for that set_params call, you can explicitly specify which class's method you want to call:

class A(object):
    def __init__(self):
        ...
        A.set_params(self)

or you can add a __set_params method that won't get overridden and call that from __init__ and set_params:

class A(object):
    def __init__(self):
        ...
        self.__set_params()
    def set_params(self):
        self.__set_params()
    def __set_params(self):
        # actual implementation here

One way, you have to type out the class name explicitly every time, and the other way, you have to write up another method definition. Which one works better depends on how long your class name is and how many times you need to use the method.

Sign up to request clarification or add additional context in comments.

1 Comment

Thanks for the answer. I agree that inheritance is tricky and more thought/effort should be spent to design and document the code more properly. I see how both suggested methods are at a cost, and I figured out a way to design the class hierarchy better to eliminate the need for such hacks in the first place.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.