Python convention: should I normally call the super class' __init__? - python

I read the highest rated answer to this question, and it says we should call the super class' __init__ if we need to, and we don't have to. But my question is more about convention.
Should I normally, as a general rule, always call the superclass' __init__ in my class' __init__, regardless of whether or not I currently 'need' the functionality in that method?

Some classes need their __init__ method to be called in order to work. They use their __init__ method sets attributes that will be needed.
Example:
class one ():
def __init__ (self):
self.number = 20
def show_number(self):
return self.number
If you inherit from the above class, you will need to call its __init__ method in order to define the attribute number. If the __init__ method is not called you could get an error when you try to call the method show_number.
As for the syntax, if nothing happens in the __init__ method of the inherited class you don't need to call it. If you think not calling the __init__ method would confuse others, you can always explain your reasoning with comments. It does not do any harm to call it even if you don't need it.

This answer has some downvotes because the downvoters disagree with me on the focus, and perhaps on what "convention" means. I think we mostly agree on the actual practice when it comes to writing code.
No. You should not normally, as a general rule, always call the superclass's __init__ in your class's __init__, regardless of whether or not you currently "need" the functionality in that method.
But please note that my emphasis is on that last phrase, starting with "regardless", and that is what my "no" answer is meant to address. You shouldn't be throwing something into your Python code "just because someone told you to" or "just because that seems to be what most people are doing".
You should include something if it is needed, and not include something if it is not.
It is very often the case, some would argue that it is normally the case, that you do want to call the superclass's __init__ method in your subclass's __init__ method. I do this myself most of the time.
But why?
Crucially, it is not because of some "convention". I do it because my subclass normally needs the same initialization as the superclass, plus a bit of extra customization. Note that the extra customization is the whole reason for overriding __init__ in the first place. If the initialization of your subclass is meant to be identical to that of the superclass, then you shouldn't be defining your own __init__ at all.
It's not a convention in Python to code something you don't need. Some people have their own conventions to include unnecessary things; perhaps in the name of "defensive programming" or because they are used to a different language in which more boilerplate is required.
Where Python's conventions come in is when you have a choice between multiple ways to express something useful. It's true that Python does not emphasize brevity above all else. But that doesn't mean it emphasizes verbosity either. So let me add this, in case it's not clear:
You should normally, as a general rule, always avoid unnecessary boilerplate code. (And not just in Python.)
[For those who think the phrase "normally always" is awkward or nonsensical: I completely agree, but I was trying to emphasize my point by repeating the asker's own choice of words.]

Yes. As a general rule you should call the superclass's __init__ from a subclass's __init__ method. This is not a Python convention, it is what OO best practice suggests you should do (given the language you happen to be using, leaves this decision up to you):
The superclass doesn't know about the subclass, but the subclass is expected to know about the semantics of the superclass it inherits from. Ultimately it is up to the subclass to maintain a consistent behavior of a true sub-typing (which unfortunately, the Python language does little to help the programmer with). You, as the subclass implementer get to decide whether or not you need to call the superclass __init__, just like you get to decide whether you do/don't need/want to call the superclass's implementation of any method you override. However, initialization of an object tends to be a pretty important step in the life-cycle of many objects. Until the object has been initialized one can argue the object is not truly an instance of the given class. It is an implied and important post-condition in ~all sane OO languages that when you instantiate an object certain things have happened and we can depend on those things having happened, initialization (or "construction" in other languages) being the central thing - whether it involves complex parameter validation and computed value generation, or is just a null op. So if you don't call super's initializer you better know exactly what your in for.
Another argument for calling super's __init__ as a general rule is, if you didn't write the superclass, or your not tightly controlling the version, the implementation of __init__ may change in future (does adding something to __init__ call for a major version bump? I'm not sure but I bet a lot of folks wouldn't bump the major version number for that, even if technically they should). So if you do call super's __init__ your code is less likely to break with updates to the superclass implementation.
Update: Should have read the linked question before answering this question. Most of the answers over there echo the general sentiment here - perhaps not in the same terms or as strongly in favor of calling __init__() as a general rule as I am. But I'll leave this answer for others that fall here.

Related

Python method call syntax shorthand

Is there a reason we call methods in python like object.method instead of Class.method(object)?
Maybe it isn't a strange choice, but personally it made understanding the self parameter much easier when I was shown the second way of calling a method.
Hardcoding the class name basically prevents you from using polymorphism. This is general OOP, not particularly a Python feature.
Your calling code should not need to know, nor care, which exact class object is.
This is immediately a problem for code where object can be a member of either Baseclass or Derivedclass, but much more complex inheritance and method overriding scenarios are possible, and sometimes necessary.

why __init__ method in python strictly considered not as a constructor

I am pretty new to Python. I just started to gather knowledge about OOP in python.
In OOP ,there is always the concept of constructor,but in python we see a different scenario. that is, it does not have a explicit constructor.
Though the __init__() method is considered a constructor in python, just not explicitly... It behaves in many ways like a constructor. e.g. it is the first lines of code which are executed. But strictly speaking, it would be wrong to call it a constructor. A new instance or the class in question, is already "constructed" by the time the method __init__ is called. Could anyone provide a good explanation with example on this line I wrote: "because a new instance is already "constructed" by the time the method __init__ is called"

Would you ever pass anything other than (MyClass, self) to super()?

I know that in Python 3, you can write super() and Python automatically passes the correct arguments to super.
It's also possible to introduce subtle bugs by accidentally writing super(Parent, self).
Are there any scenarios where you wouldn't want to pass the current class as the first argument to super?
Yes, potentially you might want to skip the immediate superclass method, but still call the methods further up the hierarchy. This might happen for example when you know you have replaced the logic with your own, and calling the superclass method would be pointless or even harmful.

Call super().__init__() in classes derived from `object`?

The Python documentation says that the __init__ method of each class is responsible for initializing its super class. But for new-style classes, the ultimate base class is object. Doing dir(object) shows that object itself has an __init__ method and could potentially be initialized. Is there any reason to do that?
I'm inclined to do it for consistency and (slightly) easier refactoring of the class heirarchy, but I wonder if it's strictly necessary or is considered best practice.
You don't need to initialize object; its __init__ is a no-op. It's still good practice, though, as you might want to introduce an intermediate class in the hierarchy later on.
Yes, do it. It's a good habit to get into, and it doesn't hurt.
IMHO it doesn't make any sense at all.
It makes you double check the inheritance to realize that it does nothing
It's the same as adding a pass statement with the overhead of function call.
Quoting the zen: Although practicality beats purity.
Python 3 doesn't require you to declare object as super class.
Yes, and there is a reason why you should do it.
If you ever need to use multi inheritance, python's C3 method resolution order (MRO) will not call all your __init__() base classes.

Is it a good idea to use super() in Python?

Or should I just explicitly reference the superclasses whose methods I want to call?
It seems brittle to repeat the names of super classes when referencing their constructors, but this page http://fuhm.net/super-harmful/ makes some good arguments against using super().
The book Expert Python Programming has discussed the topic of "super pitfalls" in chapter 3. It is worth reading. Below is the book's conclusion:
Super usage has to be consistent: In a class hierarchy, super should be used everywhere or nowhere. Mixing super and classic calls is a confusing practice. People tend to avoid super, for their code to be more explicit.
Edit: Today I read this part of the book again. I'll copy some more sentences, since super usage is tricky:
Avoid multiple inheritance in your code.
Be consistent with its usage and don't mix new-style and
old-style.
Check the class hierarchy before calling its methods in
your subclass.
You can use super, but as the article says, there are drawbacks. As long as you know them, there is no problem with using the feature. It's like people saying "use composition, not inheritance" or "never use global variables". If the feature exists, there is a reason. Just be sure to understand the why and the what and use them wisely.
super() tries to solve for you the problem of multiple inheritance; it's hard to replicate its semantics and you certainly shouldn't create any new semantics unless you're completely sure.
For single inheritance, there's really no difference between
class X(Y):
def func(self):
Y.func(self)
and
class X(Y):
def func(self):
super().func()
so I guess that's just the question of taste.
I like super() more because it allows you to change the inherited class (for example when you're refactoring and add an intermediate class) without changing it on all the methods.
The problem people have with super is more a problem of multiple inheritance. So it is a little unfair to blame super. Without super multiple inheritance is even worse. Michele Simionato nicely wrapped this up in his blog article on super:
On the other hand, one may wonder if
all super warts aren't hints of some
serious problem underlying. It may
well be that the problem is not with
super, nor with cooperative methods:
the problem may be with multiple
inheritance itself.
So the main lesson is that you should try to avoid multiple inheritance.
In the interest of consistency I always use super, even if for single inheritance it does not really matter (apart from the small advantage of not having to know the parent class name). In Python 3+ super is more convenient, so there one should definitely use super.
Yes, you should use super() over other methods. This is now the standard object inheritance model in Python 3.
Just stick to keyword arguments in your __init__ methods and you shouldn't have too many problems. Additionally you can use **kwargs to support additional parameters that are not defined in levels of the inheritance chain.
I agree that it is brittle, but no less so than using the name of the inherited class.

Categories

Resources