Base class in Python, also known as a superclass or parent class, is a class from which other classes inherit attributes (data) and methods (behaviors). It serves as a blueprint for derived (child or subclass) classes, promoting code reuse and modularity.
Key Characteristics of Base Classes:
Inheritance: Derived classes can inherit properties and methods from a base class using syntax like
class DerivedClass(BaseClass):.Code Reuse: Common functionality (e.g.,
start_engine()in aMotorVehicleclass) is defined once in the base class and reused across multiple subclasses.Method Resolution Order (MRO): Python uses MRO to determine which method or attribute to call when multiple inheritance is involved.
Initialization: If a derived class does not define its own
__init__()method, Python automatically calls the base class’s constructor.
Example:
class MotorVehicle:
def __init__(self, brand, model):
self.brand = brand
self.model = model
def start_engine(self):
print(f"The {self.brand} {self.model}'s engine is starting!")
class SelfDrivingCar(MotorVehicle):
def self_drive(self):
print("Self-driving through the road!")
# Usage
car = SelfDrivingCar("Waymo", "One")
car.start_engine() # Inherited from base class
car.self_drive() # Defined in derived classIn this example, MotorVehicle is the base class, and SelfDrivingCar is the derived class that inherits from it.
Advanced Use: Abstract Base Classes (ABCs)
A base class can be made abstract using the abc module to enforce that subclasses implement certain methods. This ensures a consistent interface across related classes.
from abc import ABC, abstractmethod
class Animal(ABC):
@abstractmethod
def speak(self):
pass
class Dog(Animal):
def speak(self):
return "Woof!"
# Attempting to instantiate Animal will raise TypeError
# animal = Animal() # ❌ TypeError: Can't instantiate abstract classUsing ABC and @abstractmethod ensures that any subclass must implement required methods, improving code robustness and maintainability.
If you ever write code that needs to process mixed types, some of which might define the API method, but which shouldn't be considered "validators", having a common base class for "true" validators might be helpful for checking types ahead of time and only processing the "true" validators, not just "things that look like validators". That said, the more complex the method name, the less likely you are to end up with this sort of confusion; the odds of a different type implementing a really niche method name by coincidence are pretty small.
That said, that's a really niche use case. Often, as you can see, Python's duck-typing behavior is good enough on its own; if there is no common functionality between the different types, the only meaningful advantages to having the abstract base class are for the developer, not the program itself:
- Having a common point of reference for the API consumer makes it clear what methods they can expect to have access too
- For actual ABCs with
@abstractmethoddecorated methods, it provides a definition-time check for subclasses, making it impossible for maintainers to omit a method by accident
oop - What is the purpose of base classes in Python if you're already referring to the implementation classes consistently - Stack Overflow
inheritance - Why "object" is passed as base class in python? - Stack Overflow
python - List all base classes in a hierarchy of given class? - Stack Overflow
In inheritance, is it considered bad practice to define a base class which cannot be used except for when defining derived classes?
Videos
If you ever write code that needs to process mixed types, some of which might define the API method, but which shouldn't be considered "validators", having a common base class for "true" validators might be helpful for checking types ahead of time and only processing the "true" validators, not just "things that look like validators". That said, the more complex the method name, the less likely you are to end up with this sort of confusion; the odds of a different type implementing a really niche method name by coincidence are pretty small.
That said, that's a really niche use case. Often, as you can see, Python's duck-typing behavior is good enough on its own; if there is no common functionality between the different types, the only meaningful advantages to having the abstract base class are for the developer, not the program itself:
- Having a common point of reference for the API consumer makes it clear what methods they can expect to have access too
- For actual ABCs with
@abstractmethoddecorated methods, it provides a definition-time check for subclasses, making it impossible for maintainers to omit a method by accident
Base classes in Python serve to share implementation details and document likenesses. We don't need to use a common base class for things that function similarly though, as we use protocols and duck typing. For these validation functions, we might not have a use for a class at all; Java is designed to force people to put everything inside classes, but if all you have in your class is one static method, the class is just noise. Having a common superclass would enable you to check dynamically for that using isinstance, but then I'd really wonder why you're handling them in a context where you don't know what they are. For a programmer, the common word in the function name is probably enough.
inspect.getmro(cls) works for both new and old style classes and returns the same as NewClass.mro(): a list of the class and all its ancestor classes, in the order used for method resolution.
>>> class A(object):
>>> pass
>>>
>>> class B(A):
>>> pass
>>>
>>> import inspect
>>> inspect.getmro(B)
(<class '__main__.B'>, <class '__main__.A'>, <type 'object'>)
See the __bases__ property available on a python class, which contains a tuple of the bases classes:
>>> def classlookup(cls):
... c = list(cls.__bases__)
... for base in c:
... c.extend(classlookup(base))
... return c
...
>>> class A: pass
...
>>> class B(A): pass
...
>>> class C(object, B): pass
...
>>> classlookup(C)
[<type 'object'>, <class __main__.B at 0x00AB7300>, <class __main__.A at 0x00A6D630>]
I am working on a few projects at the moment which I think could really benefit from inheritance. However, the derived classes are really not that similar except for a couple of methods that they both require.
In this case, is it considered bad practice to define a base class that cannot be used by itself, but can only be used in the context of inheritance? Here's a stripped-back (and pointless) example of what I mean:
class _incompleteBase:
def __init__(self):
pass
def common_method(self):
print(self.val)
class child1(_incompleteBase):
def __init__(self, val):
super().__init__()
# Definitions only required by child1
self.val = val
class child2(_incompleteBase):
def __init__(self, val):
super().__init__()
# Definitions only required by child2
self.val = val
if __name__ == "__main__":
c1 = child1(6)
c2 = child2(7)
c1.common_method()
c2.common_method()Here I have _incompleteBase, which is inherited by derived classes child1 and child2. This code works for me. However, the following will obviously throw an error:
# AttributeError: '_incompleteBase' object has no attribute 'val'
if __name__ == "__main__":
ib = _incompleteBase()
ib.common_method()_incompleteBase will never be used like this in my projects, and to do so would be a 'misuse' of this class as far as I'm concerned.
However, I'd be interested in hearing other people's views on this. Is this OK to do, or is this bad practice?