Java and C++ require that a base class constructor is called because of memory layout.
If you have a class
BaseClass with a member
field1, and you create a new class
SubClass that adds a member
field2, then an instance of
SubClass contains space for
field2. You need a constructor of
BaseClass to fill in
field1, unless you require all inheriting classes to repeat
BaseClass's initialization in their own constructors. And if
field1 is private, then inheriting classes can't initialise
Python is not Java or C++. All instances of all user-defined classes have the same 'shape'. They're basically just dictionaries in which attributes can be inserted. Before any initialisation has been done, all instances of all user-defined classes are almost exactly the same; they're just places to store attributes that aren't storing any yet.
So it makes perfect sense for a Python subclass not to call its base class constructor. It could just add the attributes itself if it wanted to. There's no space reserved for a given number of fields for each class in the hierarchy, and there's no difference between an attribute added by code from a
BaseClass method and an attribute added by code from a
If, as is common,
SubClass actually does want to have all of
BaseClass's invariants set up before it goes on to do its own customisation, then yes you can just call
BaseClass.__init__() (or use
super, but that's complicated and has its own problems sometimes). But you don't have to. And you can do it before, or after, or with different arguments. Hell, if you wanted you could call the
BaseClass.__init__ from another method entirely than
__init__; maybe you have some bizarre lazy initialization thing going.
Python achieves this flexibility by keeping things simple. You initialise objects by writing an
__init__ method that sets attributes on
self. That's it. It behaves exactly like a method, because it is exactly a method. There are no other strange and unintuitive rules about things having to be done first, or things that will automatically happen if you don't do other things. The only purpose it needs to serve is to be a hook to execute during object initialisation to set initial attribute values, and it does just that. If you want it to do something else, you explicitly write that in your code.