Class hierarchies and isinstance
Inheritance can go on for more than one level. A derived class can be the base class of another class, and a base class can have many derived classes. Together they form a class hierarchy, which you can draw like a family tree. Here bicycles and cars are vehicles, and an electric car is a special kind of car:
Vehicle
Bicycle
Car
ElectricCar
An ElectricCar inherits everything from Car, including everything Car inherited from Vehicle:
class Vehicle:
def __init__(self, name):
self.name = name
def describe(self):
return f"{self.name} moves"
class Bicycle(Vehicle):
def describe(self):
return f"{self.name} is pedaled"
class Car(Vehicle):
def honk(self):
return f"{self.name}: beep beep"
class ElectricCar(Car):
def __init__(self, name, battery):
super().__init__(name)
self.battery = battery
def charge(self):
self.battery = 100
spark = ElectricCar("Spark", 35)
print(spark.describe())
print(spark.honk())
spark.charge()
print(spark.battery)
Spark moves
Spark: beep beep
100To find describe, Python looks in ElectricCar, then in Car, then in Vehicle, and uses the first one it finds. super().__init__(name) in ElectricCar runs the constructor Car inherited, the one in Vehicle.
isinstance
Sometimes a program needs to know what kind of object it has. The function isinstance(value, class) tells you: it returns True if the value is an object of that class, or of a class derived from it. It fits the "is a" test: an electric car is a car, and it's a vehicle too. A list can hold objects of different classes of one hierarchy, and isinstance picks out the ones you need:
class Vehicle:
def __init__(self, name):
self.name = name
class Bicycle(Vehicle):
pass
class Car(Vehicle):
pass
class ElectricCar(Car):
pass
spark = ElectricCar("Spark")
print(isinstance(spark, ElectricCar))
print(isinstance(spark, Car))
print(isinstance(spark, Vehicle))
print(isinstance(spark, Bicycle))
garage = [Car("Rusty"), Bicycle("Fixie"), spark, ElectricCar("Volt")]
for vehicle in garage:
if isinstance(vehicle, Car):
print(vehicle.name, "needs a parking space")
True
True
True
False
Rusty needs a parking space
Spark needs a parking space
Volt needs a parking spaceThe electric cars count as cars, so they need a parking space too. The bicycle is a vehicle but not a car, so it's left out. Checking type(vehicle) == Car instead would leave out the electric cars as well: type gives only the object's own class, while isinstance follows the hierarchy.
isinstance works with Python's own types too:
>>> isinstance(42, int)
True
>>> isinstance("42", int)
False
>>> isinstance([1, 2], list)
TrueEvery class you write is part of a hierarchy as well. A class with no base class in parentheses inherits Python's built-in class object. That's where the methods you never wrote come from, such as the plain __str__ that prints an object's address.