Why call a base class constructor from inherited class - python

I have been trying to understand this use case, where we often call a base class constructor from the inherited class, is the sole purpose of doing that is to just ensure that the base class is initialized? Or, would there be other possible use cases?
class Base:
def __init__(self):
print('Base.__init__')
class A(Base):
def __init__(self):
super().__init__()
print('A.__init__')

is the sole purpose of doing that is to just ensure that the base class is initialized?
Well yes, but what do you mean, just?
Assuming your base class has a reason to exist, it must do something.
Unless it's just a convenient collection of #staticmethod functions, whatever it does might depend on its __init__ having been called, because that's how class objects work.
Even if your base class has an empty __init__ today, it's sensible to call it, in case that changes in the future.
Or, would there be other possible use cases?
The use case is to make sure that the base class part of your object is correctly initialized. Without that, you can't safely call any of its non-static methods.
In principle your base class could do something tricksy in its __init__ (starting a background thread, or registering the instance with some singleton, or ... whatever). So yes, there could be effects other than just assigning instance variables, but this is still part of initializing an object of that base class.

In C++ or Java, the compiler will require you to call the base class constructor (either by automatically inserting a zero-argument call or by giving you an error).
Python requires you to call it yourself, as it is written:
If a base class has an __init__() method, the derived class’s __init__() method, if any, must explicitly call it to ensure proper initialization of the base class part of the instance
The reason why is a principle of object oriented design. An A "is-a" Base, which could also be written equivalently as an A "has-a" Base. Unless you specifically want to interfere with the implementation of Base, you have to allow the object to be initialized as designed. Skipping the constructor will leave the Base object improperly initialized, disappointing anyone who expects it to behave as a Base object ought to.
When overriding a method besides the constructor, it is the programmer's discretion to delegate to the base class implementation or to override it entirely. This can still lead to incorrect behavior --- several API docs I can think of pepper the documentation with "If you override this method, you should call super" for various methods.

The point of this is to initialize all the stuff the base class usually initializes. For example,
class Base:
def __init__(self, number):
print('Base.__init__')
self.number = number
class A(Base):
def __init__(self, number, string):
super().__init__(number)
self.string = string
print('A.__init__')
In this code example it's more obvious. When A calls the base constructor, the base constructor will initialize all of the stuff needed, such as self.number. This way, the rest of A's initialization function can build on top of the base initialization function without any retyping. In this example, A is building on top of Base by adding self.string on top of self.number.

Related

__subclasses__ or registry via __init_subclass__?

Let's say I want to create a registry of subclasses of a certain class. Now there are two approaches I can think of and while I'm aware of (some of) their differences, I'd love to learn more about the topic.
class Base:
pass
class DerivedA(Base):
pass
class DerivedB(Base):
pass
__subclasses__()
If I have the situation above, I can simply get the list of subclasses of Base like this:
>>> [cmd.__name__ for cmd in Base.__subclasses__()]
['DerivedA', 'DerivedB']
Now I'm aware that if I add a third class that is not directly subclassing Base like this:
class DerivedC(DerivedA):
pass
I will not see this one in the list:
>>> [cmd.__name__ for cmd in Base.__subclasses__()]
['DerivedA', 'DerivedB']
Also I can't filter the subclasses and for example ignore a particular subclass for any reason.
__init_subclass__()
Since Python 3.6 there is a nice hook into class creating process and more advanced things can be done without writing one's own metaclass. Thus I can also do something like this...
_registry = []
class Base:
def __init_subclass__(cls, **kwargs):
super().__init_subclass__(**kwargs)
_registry.append(cls.__name__)
class DerivedA(Base):
pass
class DerivedB(Base):
pass
class DerivedC(DerivedA):
pass
And then simply access _registry:
>>> _registry
['DerivedA', 'DerivedB', 'DerivedC']
I can also modify Base to ignore certain subclasses if I wanted:
_registry = []
class Base:
def __init_subclass__(cls, **kwargs):
super().__init_subclass__(**kwargs)
if cls.__name__ != 'DerivedB':
_registry.append(cls.__name__)
class DerivedA(Base):
pass
class DerivedB(Base):
pass
class DerivedC(DerivedA):
pass
>>> _registry
['DerivedA', 'DerivedC']
Why use the latter?
Now let's say that I don't want to filter the subclasses and I'm only interested in direct subclasses. The former approach seems to be simpler (subjective, I know). What are other differences and maybe what are the advantages of the latter approach?
Thanks!
The obvious gain of writing __init_subclass__ in a base class in this case is that you can automatically get to the subclasses that do not inherit directly from your base class, as you put it.
If you only need the classes that inherit directly from your base, then it is ready in the __subclasses__ method, and the major advantage is that you don't need to write a single line of code, and not even keep a separate registry, as the __subclasses__ will do that for you.
However, unless you are writing a relatively small app, or are dealing with a feature that just needs a small fixed number of these subclasses to be looked-up, relying in __subclasses__ is not enough - if you simply need, or want, another level of classes in your hierarchy, it will stop working, and you have to resort to a true registry anyway.
Prior to having the __init_subclass__ hook, one would have to write a proper metaclass to keep this registry, feeding it on the metaclass __init__ method, or do a complicated recursive query like:
def check_subclass(base, candidate):
for cls in base.__subclasses__():
if cls is candidate:
return True
if check_subclass(cls, candidate):
return True
return False
And, although it should go without saying, the __init_subclass__ method can do a lot more than simply keep a registry - as it can run any code. It could check against the DB layer if the fields mapped to that subclass are up to date, and warn of a needed migration - or even perform the DB migration itself, or initialise any resources that instances of the class will need to find ready when they are created, such as logger-handlers, thread-pools, db-connection pools, you name it.
TL;DR: If you just need the direct subclasses of a class, go with __subclasses__. The catch is exactly that it just annotates the direct subclasses.

How to call non abstract method in a abstract class?

I have an abstract class in python and want to call non-abstract methods in it. Is it possible to do it?
from abc import ABC, abstractmethod
class MyAbstract(ABC):
# Can I call method get_state() from get_current() ?
def get_state():
get_current() # gives me error?
def get_current():
#abstractmethod
def get_time():
I have another python file, Temp.py implement this interface.
In Temp.py, I call the get_state using MyAbstract.get_state(), I get the error stating that get_current() is undefined.
Not sure why.
Any help is appreciated.
In general, all methods have a namespace which is the class or object they're attached to. If you have an instance of a class floating around (e.g. self, most of the time), you can call methods on that instance that automatically pass the instance itself as the first parameter - the instance acts as the namespace for an instance method.
If you're using a class method or a static method, then the namespace is almost always going to be the class they're attached to. If you don't specify a namespace, then python assumes that whatever function you're trying to call is in the global namespace, and if it isn't, then you get a NameError.
In this case, the following should work for you:
class MyAbstract(ABC):
def get_current():
print("current")
def get_state():
MyAbstract.get_current()
#abstractmethod
def get_time():
pass
You can just imagine that you have a little invisible #staticmethod decorator hanging above get_current() that marks it as such. The problem with this is that now you don't get to change the behavior of get_current() in subclasses to affect change in get_state(). The solution to this is to make get_state() a class method:
#classmethod
def get_state(cls):
cls.get_current()
Calling a static method uses identical syntax to calling a class method (in both cases you would do MyAbstract.get_state(), but the latter passes the class you're calling it on as the first argument. You can then use this class as a namespace to find the method get_current() for whatever subclass has most recently defined it, which is how you implement polymorphism with method that would otherwise be static.

Why is __init__ apparently optional?

While experimenting, I wrote:
class Bag:
pass
g = Bag()
print(g)
Which gave me:
<__main__.Bag object at 0x00000000036F0748>
Which surprised me. I expected an error when I tried to initialize it, since I didn't define __init___.
Why isn't this the case?
You only need to override the methods you want to change.
In other words:
If you don't override __init__, the __init__ method of the superclass will be called.
E.g.
class Bag:
pass
if equivalent to:
class Bag:
def __init__(self):
super(Bag, self).__init__()
Furthermore, __init__ is indeed optional. It is an initializer for an instance.
When you instantiate a class (by calling it) the constructor for the class (class method __new__) is called. The constructor returns an instance for which __init__ is called.
So in practice even:
class Bag:
def __init__(self):
pass
Will work just fine.
__init__ is an intializer not the constructor, If an __init__ method is defined it is used just to initialize the created object with the values provided as arguments. An object anyhow gets created even if an __init__ method is not defined for the class, however not initialized, as __init__ method is not overridden to customize as per your needs.
You do not need to include the __init__ method if you do not intend on adding/changing it's functionality. Everything in python is an object and python objects have a number of built in methods, some of which you may include when creating your own object and some of which you may not. This is not a bad reference for learning about built in methods.
http://www.rafekettler.com/magicmethods.html
I might add one thing. If you intend on using the super function, it is a good idea to define objects which inherit from object. Perhaps this is not required in python 3+ but it is certainly true for some of the older versions.
class Bag(object):
def __init__(self):
super(Bag, self).__init__()
# Adding extra functionality here.

discriminating whether python object is subclass of current __init__ method

This is an odd one. I have run into a situation a few times where I would like to do some action at the end of __init__ of a python object. I would like to do the action as the last thing in the __init__ of the true class (leaf class) only. I absolutely do not want to do it if in a superclass of self __init__ method. Is there a good way to do this? I grant it may be a silly thing to do. But it has me curious whether and how it could be done.
If you want some code to run in the leaf class, at the end of __init__, without it running in the base class… just override __init__ in the leaf class. For example:
class Base(object):
def __init__(self):
print('Base initializing')
class Intermediate(Base):
pass
class Leaf(Intermediate):
def __init__(self):
super(Leaf, self).__init__()
print('Leaf initializing')
>>> o = Base()
Base initializing
>>> o = Intermediate()
Base initializing
>>> o = Leaf()
Base initializing
Leaf initializing
If you're trying to programmatically detect, from within a method like Base.__init__, whether self is a Base or some subclass of Base… well, usually this is a bad idea, and you actually want to use method overriding, as above. But it can be done easily. For example:
class Base(object):
def __init__(self):
if type(self) != Base:
print('Some subclass of Base initializing')
class Leaf(Base):
pass
>>> obj = Leaf()
Some subclass of Base initializing
(If you're worried that someone might subvert your hierarchy such that some object that's neither a Base nor a subclass of it might end up calling Base.__init__, and you're sure that would be an evil thing rather than a clever monkeypatching thing, you can always check issubclass.)
If you're trying to programmatically detect, within a method like Base.__init__, whether base is a "real class"… well, you need to define what that means. Does it mean "Not an abstract base class in the PEP3119 sense"? "Has no subclasses defined (yet)"? "Has an implementation for some critical method"? Whatever you want to do is probably doable, but some will be harder than others. (For example, to detect whether anyone has subclassed your class, you'll likely need to build a custom metaclass.)

Passing parameter to base class constructor or using instance variable?

All classes derived from a certain base class have to define an attribute called "path". In the sense of duck typing I could rely upon definition in the subclasses:
class Base:
pass # no "path" variable here
def Sub(Base):
def __init__(self):
self.path = "something/"
Another possiblity would be to use the base class constructor:
class Base:
def __init__(self, path):
self.path = path
def Sub(Base):
def __init__(self):
super().__init__("something/")
I use Python 3.1.
What would you prefer and why? Is there a better way?
In Python 3.0+:
I would go with a parameter to the base class's constructor like you have in the second example. As this forces classes which derive from Base to provide the necessary path property, which documents the fact that the class has such a property and that derived classes are required to provide it. Without it, you would be relying on this being stated (and read) somewhere in your class's docstrings, although it certainly does help to also state in the docstring what the particular property means.
In Python 2.6+:
I would use neither of the above; instead I would use:
class Base(object):
def __init__(self,path):
self.path=path;
class Sub(Base):
def __init__(self):
Base.__init__(self,"something/")
In other words, I would require such a parameter in the base class's constructor, because it documents the fact that all such types will have/use/need that particular parameter and that the parameter needs to be provieded. However, I would not use super() as super is somewhat fragile and dangerous in Python, and I would also make Base a new-style class by inheriting from object (or from some other new-style) class.

Categories

Resources