1

Python 3.6

I just found myself programming this type of inheritance structure (below). Where a sub class is calling methods and attributes of an object a parent has.

In my use case I'm placing code in class A that would otherwise be ugly in class B.

Almost like a reverse inheritance call or something, which doesn't seem like a good idea... (Pycharm doesn't seem to like it)

Can someone please explain what is best practice in this scenario?

Thanks!

class A(object):

    def call_class_c_method(self):
        self.class_c.do_something(self)

class B(A):

    def __init__(self, class_c):
        self.class_c = class_c
        self.begin_task()

    def begin_task(self):
        self.call_class_c_method()

class C(object):

    def do_something(self):
        print("I'm doing something super() useful")

a = A
c = C
b = B(c)

outputs:

I'm doing something super() useful
13
  • Why you have class C when you can put do_something in class A directly ? Commented Jul 5, 2017 at 10:53
  • 'In my use case I'm placing code in class A that would otherwise be ugly in class B' Commented Jul 5, 2017 at 10:53
  • Any reason why would do something like this? It's a very definition of an anti-pattern. Commented Jul 5, 2017 at 10:54
  • 1
    If you're not using the class A for anything (which you can't really do because it tries to reference attributes it doesn't have), there is no reason to have it. In fact, to me having the call_class_c_method method be in A and not B is orders of magnitude more ugly. Commented Jul 5, 2017 at 10:56
  • 1
    Somewhat unrelated, you are passing around a bunch of class objects instead of instances of those classes. a = A(); c = C(); b = B(c) Commented Jul 5, 2017 at 11:35

2 Answers 2

2

There is nothing wrong with implementing a small feature in class A and use it as a base class for B. This pattern is known as mixin in Python. It makes a lot of sense if you want to re-use A or want to compose B from many such optional features.

But make sure your mixin is complete in itself!

The original implementation of class A depends on the derived class to set a member variable. This is a particularly ugly approach. Better define class_c as a member of A where it is used:

class A(object):

    def __init__(self, class_c):
        self.class_c = class_c

    def call_class_c_method(self):
        self.class_c.do_something()

class B(A):

    def __init__(self, class_c):
        super().__init__(class_c)
        self.begin_task()

    def begin_task(self):
        self.call_class_c_method()

class C(object):
    def do_something(self):
        print("I'm doing something super() useful")

c = C()
b = B(c)
Sign up to request clarification or add additional context in comments.

Comments

1

I find that reducing things to abstract letters in cases like this makes it harder for me to reason about whether the interaction makes sense. In effect, you're asking whether it is reasonable for a class(A) to depend on a member that conforms to a given interface (C). The answer is that there are cases where it clearly does. As an example, consider the model-view-controller pattern in web application design. You might well have something like

class Controller:

    def get(self, request)
        return self.view.render(self, request)

or similar. Then elsewhere you'd have some code that found the view and populated self.view in the controller. Typical examples of doing that include some routing lookups or include having a specific view associated with a controller. While not Python, the Rails web framework does a lot of this.

When we have specific examples, it's a lot easier to reason about whether the abstractions make sense.

In the above example, the controller interface depends on having access to some instance of the view interface to do its work. The controller instance encapsulates an instance that implements that view interface.

Here are some things to consider when evaluating such designs:

  • Can you clearly articulate the boundaries of each interface/class? That is, can you explain what the controller's job is and what the view's job is?

  • Does your decision to encapsulate an instance agree with those scopes?

  • Do the interface and class scopes seem reasonable when you think about future extensibility and about minimizing the scope of code changes?

1 Comment

Yes, you explained what I was trying to achieve in a way that makes a lot more sense. I'm going to accept kazemakase answer, as it explains how to implement such a thing. Though, this post has given me something to take away and understand in my own time. Thanks!

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.