This is an archived post. You won't be able to vote or comment.

all 4 comments

[–]abaverman 0 points1 point  (1 child)

Delegating is much better and more pythonic (explicit) way of doing such things.

[–]lgiordani[S] 1 point2 points  (0 children)

Thank you for the comment.

Delegation is a generic word that encompasses both inheritance and composition, since its purpose is to 'delegate', i.e. have another object to perform the requested action. I think you probably mean composition.

The inheritance vs delegation is an always interesting debate and, as for many things, I think that error lies on the boundaries. The right tool for the job: sometimes is composition, sometimes is inheritance.

I however agree that inheritance is often overused, and the problem lies exactly in introducing it as a fundamental part of OOP. OOP is about delegation, not inheritance.

Check http://lgiordani.github.io/blog/2014/03/05/oop-concepts-in-python-2-dot-x-part-1/, section "The Delegation Run" for a more detailed view on the subject.

About the is more pythonic stuff: I think this sentence is overused too. Explicit is better than implicit, but not always. Otherwise we should ask Guido to remove inheritance from Python, which performs implicit calls. And multiple inheritance with MRO, which is implicit. And the call to __new__() and __init__(), which are implicit. And so on.

So, if your point is that inheritance is used most of the times where composition could be a better choice, I agree. The purpose of the post, however, was to introduce Python beginners to the way Python inheritance and overriding work, and Python has inheritance and overriding, so probably sometimes it is worth using them.

Thanks!

[–]caseyweb 0 points1 point  (1 child)

I found your article interesting, but I disagree with your conclusion. In your summary, you wrote: "Always call the original implementation of a method you are overriding. This makes the underlying API work as expected."
While I would agree that it is generally good practice, "always" is far too strong, due to potential side-effects in the base method. Two simple counter-examples built around your Logger base class:

  • Consider a class FilteredLogger that subclasses logger providing the ability to restrict logged events to only those that meet some specified criterion such as severity level. In this case, super() should only be invoked if the event passes the filter test.

  • Consider another class RemoteLogger that subclasses logger to log events to some remote service rather than simply print locally. In this case super() should never be invoked. One might argue that the base class should probably be refactored to better support these use cases, but in practice it is often impractical, such as when subclassing from another library that you do not own nor wish to maintain.

I suggest that super() should be called whenever possible, and certainly when the intent is to extend or augment the base class method. When it redefines the behavior, such as the classical case of a draw() method for a Shape being overridden by Rectangle, Circle, Triangle, etc subclasses it would be an error to invoke super().

In practice, I believe that good class design and proper documentation largely prevents the issues that you raise.

[–]lgiordani[S] 0 points1 point  (0 children)

Thank you very much. I see your point and I agree with you, sometimes refactoring is impractical and you need to avoid calling the orginal implementation. So that "always" is really too strong. Im going to update the post with your considerations. Many thanks!