runtime
131 / 250
lesson 131 Inheritance & Composition · advanced

Inheritance: a class that builds on another

When one kind of object is a special case of another (a book is a product), a subclass inherits everything the parent has and adds or changes only what is different.

You'll be able to
  • Write class Child(Parent): and use the parent's __init__ and methods on child objects
  • Change a class attribute or add a method in the subclass only
  • Use isinstance and issubclass, and catch a family of your own exceptions by their parent class
The idea

Runtime Market sells ordinary products, books and downloads. They are all products (an SKU, a name, a price), but each kind differs a little: books carry no VAT, downloads aren't shipped. Copying the Product class three times and editing each copy would mean fixing every bug three times.

class Book(Product): creates a subclass. A Book object has everything a Product has: the same __init__, attributes and methods. In the subclass body you write only what is different. When Python looks up an attribute or method on a Book, it checks the Book class first and then its parent, so a class attribute defined in Book replaces the parent's for books only.

isinstance(novel, Product) is True for a Book: a book *is a* product, and code written for products accepts it.

Run each example to see what it prints. Running examples doesn't count toward progress.

example-1.py · Books without VAT
class Product:
    VAT_RATE = 0.2

    def __init__(self, sku, name, price):
        self.sku = sku
        self.name = name
        self.price = price

    def price_with_vat(self):
        return round(self.price * (1 + self.VAT_RATE), 2)

class Book(Product):
    VAT_RATE = 0.0

lamp = Product("HOM-22847", "Desk lamp", 48.0)
novel = Book("BOO-10233", "The Long Road", 8.99)
print(lamp.price_with_vat(), novel.price_with_vat())
print(novel.name, type(novel).__name__)
print(isinstance(novel, Product), isinstance(lamp, Book))

A subclass can also add methods. They exist only on the subclass: inheritance goes from parent to child, never the other way.

example-2.py · A child's method on a parent object
class Product:
    def __init__(self, sku, name, price):
        self.sku = sku
        self.name = name
        self.price = price

class DigitalProduct(Product):
    def download_url(self):
        return "https://downloads.runtime.market/" + self.sku.lower()

ebook = DigitalProduct("DIG-30115", "Python Basics (e-book)", 12.0)
print(ebook.download_url())
lamp = Product("HOM-22847", "Desk lamp", 48.0)
print(lamp.download_url())
example-3.py · A basket with downloads in it
class Product:
    SHIPS = True

    def __init__(self, sku, name, price):
        self.sku = sku
        self.name = name
        self.price = price

class DigitalProduct(Product):
    SHIPS = False

    def download_url(self):
        return "https://downloads.runtime.market/" + self.sku.lower()

basket = [
    Product("HOM-22847", "Desk lamp", 48.0),
    DigitalProduct("DIG-30115", "Python Basics (e-book)", 12.0),
    Product("SPO-11681", "Yoga mat", 31.0),
]
to_ship = [item.sku for item in basket if item.SHIPS]
print("ship:", to_ship)
for item in basket:
    if isinstance(item, DigitalProduct):
        print("email link:", item.download_url())
print(issubclass(DigitalProduct, Product), issubclass(Product, DigitalProduct))

You have used inheritance since the Exceptions & Validation module: class QuantityError(Exception) made a subclass of Exception. Exception families work the same way: except catches the class you name and all its subclasses, which is why a program can define ImportProblem and catch BadRow and MissingColumn with one except ImportProblem.

When to use it: when the new class really is a special kind of the parent and should be usable anywhere the parent is. Mistakes to avoid: inheriting just to reuse a few lines of code from an unrelated class (the Inheritance & Composition module shows the alternative); calling a subclass-only method on objects that may be the parent; and long isinstance chains to decide behaviour (the next lessons replace them).

Review
  • class Child(Parent): inherits the parent's __init__, methods and class attributes.
  • Lookup checks the object's class first, then its parents, so the subclass only states what differs.
  • isinstance(obj, Parent) is True for subclass objects; except Parent catches every subclass error.
  • Next: replacing a parent's method in a subclass, and extending it with super().
Practise this

Loading your progress…

To master this lesson, solve both challenges without hints or the answer, and pass the concept check.

Inheritance: a class that builds on another — Inheritance & Composition · Runtime