{"id":"inheritance-base-class","slug":"base-class","title":"Inheriting from a base class","text":"Some classes turn out to be almost the same. Say you're writing a small adventure game with wizards and knights. Both have a name and health points, both can take damage, and both print the same way. Only one thing differs: a wizard casts spells and a knight raises a shield. Written as two separate classes, much of the code is there twice:\n\n```python\nclass Wizard:\n    def __init__(self, name, health):\n        self.name = name\n        self.health = health\n\n    def take_damage(self, amount):\n        self.health -= amount\n\n    def cast_spell(self):\n        return f\"{self.name} casts a fireball!\"\n\n\nclass Knight:\n    def __init__(self, name, health):\n        self.name = name\n        self.health = health\n\n    def take_damage(self, amount):\n        self.health -= amount\n\n    def raise_shield(self):\n        return f\"{self.name} raises a shield.\"\n```\n\nCode that is written twice has to be fixed twice, and sooner or later one copy gets fixed and the other doesn't. Merging the two into one class with every method isn't a good answer either: then every knight could cast spells.\n\n### A base class and derived classes\n\n**Inheritance** solves this. You write what the classes share once, in a **base class**, and each special class **inherits** everything from it and adds only what is its own. To make a class inherit another, write the base class's name in parentheses after the new class's name:\n\n```python\nclass Character:\n    def __init__(self, name, health):\n        self.name = name\n        self.health = health\n\n    def take_damage(self, amount):\n        self.health -= amount\n\n    def __str__(self):\n        return f\"{self.name} ({self.health} health)\"\n\n\nclass Wizard(Character):\n    def cast_spell(self):\n        return f\"{self.name} casts a fireball!\"\n\n\nclass Knight(Character):\n    def raise_shield(self):\n        return f\"{self.name} raises a shield.\"\n\n\nelva = Wizard(\"Elva\", 30)\nbram = Knight(\"Bram\", 50)\nbram.take_damage(12)\nprint(elva)\nprint(bram)\nprint(elva.cast_spell())\nprint(bram.raise_shield())\n```\n\n```output\nElva (30 health)\nBram (38 health)\nElva casts a fireball!\nBram raises a shield.\n```\n\n`Wizard` and `Knight` are **derived classes** of `Character`. (You'll also hear *subclass* or *child class* for a derived class, and *superclass* or *parent class* for the base class.) Neither has a constructor of its own, so `Wizard(\"Elva\", 30)` runs the constructor it inherits from `Character`. `take_damage` and `__str__` are written once, but they work for both. And `cast_spell` can use `self.name`, because every wizard got its name from that same constructor.\n\n### Inheritance goes one way\n\nA derived class gets everything its base class has, but not the other way around. A plain `Character` has no `cast_spell`:\n\n```python\nclass Character:\n    def __init__(self, name):\n        self.name = name\n\n\nclass Wizard(Character):\n    def cast_spell(self):\n        return f\"{self.name} casts a fireball!\"\n\n\nelva = Wizard(\"Elva\")\nprint(elva.cast_spell())\npip = Character(\"Pip\")\nprint(pip.cast_spell())\n```\n\n```output\nElva casts a fireball!\n```\n\n```crash\nTraceback (most recent call last):\n  File \"main.py\", line 14, in <module>\n    print(pip.cast_spell())\n          ^^^^^^^^^^^^^^\nAttributeError: 'Character' object has no attribute 'cast_spell'\n```\n\nTwo derived classes don't share their own methods either: a knight can't cast spells. A quick test of a design is the words \"is a\". A wizard is a character, so `Wizard(Character)` makes sense. A character isn't always a wizard, so the spells belong in `Wizard` only.\n","task":"A truck is a vehicle that can carry a load. Fill in the blank so that `Truck` inherits `Vehicle`, and the program prints:\n\n```\nTRK-42, 6 wheels\nTRK-42 carries 12 tons\n```\n","kind":"fill","seed":"class Vehicle:\n    def __init__(self, plate, wheels):\n        self.plate = plate\n        self.wheels = wheels\n\n    def __str__(self):\n        return f\"{self.plate}, {self.wheels} wheels\"\n\n\nclass Truck([[Vehicle]]):\n    def load(self, tons):\n        return f\"{self.plate} carries {tons} tons\"\n\n\nif __name__ == \"__main__\":\n    truck = Truck(\"TRK-42\", 6)\n    print(truck)\n    print(truck.load(12))\n","solution":"class Vehicle:\n    def __init__(self, plate, wheels):\n        self.plate = plate\n        self.wheels = wheels\n\n    def __str__(self):\n        return f\"{self.plate}, {self.wheels} wheels\"\n\n\nclass Truck(Vehicle):\n    def load(self, tons):\n        return f\"{self.plate} carries {tons} tons\"\n\n\nif __name__ == \"__main__\":\n    truck = Truck(\"TRK-42\", 6)\n    print(truck)\n    print(truck.load(12))\n","hints":["The base class's name goes in the parentheses after the new class's name.","`Truck` should inherit `Vehicle`, so the line is `class Truck(Vehicle):`."],"tests":[{"name":"Output","stdin":"","expect":"TRK-42, 6 wheels\nTRK-42 carries 12 tons","hidden":false}],"require":[],"forbid":[],"more":[{"task":"A healer is a character who can heal others. Write a class `Healer` that inherits `Character`, with one method of its own: `heal(other)` adds 10 to the health of the character `other`. Don't write a constructor or a `__str__` for `Healer`: it inherits them. For example,\n\n```python\niris = Healer(\"Iris\", 20)\ntom = Character(\"Tom\", 15)\niris.heal(tom)\niris.heal(tom)\nprint(tom)\nprint(iris)\n```\n\nshould print:\n\n```\nTom (35 health)\nIris (20 health)\n```\n","kind":"code","seed":"class Character:\n    def __init__(self, name, health):\n        self.name = name\n        self.health = health\n\n    def take_damage(self, amount):\n        self.health -= amount\n\n    def __str__(self):\n        return f\"{self.name} ({self.health} health)\"\n\n\n# Write the class Healer here.\n\n\nif __name__ == \"__main__\":\n    iris = Healer(\"Iris\", 20)\n    tom = Character(\"Tom\", 15)\n    iris.heal(tom)\n    print(tom)\n","solution":"class Character:\n    def __init__(self, name, health):\n        self.name = name\n        self.health = health\n\n    def take_damage(self, amount):\n        self.health -= amount\n\n    def __str__(self):\n        return f\"{self.name} ({self.health} health)\"\n\n\nclass Healer(Character):\n    def heal(self, other):\n        other.health += 10\n\n\nif __name__ == \"__main__\":\n    iris = Healer(\"Iris\", 20)\n    tom = Character(\"Tom\", 15)\n    iris.heal(tom)\n    print(tom)\n","hints":["Start with the class line: `class Healer(Character):`. Its only method is `heal`, with the parameters `self` and `other`.","`other` is another character object, so its health is `other.health`. Add 10 to it with `+=`.","The healer's own health doesn't change, and `heal` doesn't need to return anything."],"tests":[{"name":"Test 1","stdin":"","call":"iris = Healer(\"Iris\", 20)\ntom = Character(\"Tom\", 15)\niris.heal(tom)\niris.heal(tom)\nprint(tom)\nprint(iris)\n","expect":"Tom (35 health)\nIris (20 health)","hidden":false},{"name":"Test 2","stdin":"","call":"mae = Healer(\"Mae\", 8)\nrook = Character(\"Rook\", 40)\nrook.take_damage(25)\nmae.heal(rook)\nmae.heal(mae)\nprint(rook)\nprint(mae)\n","expect":"Rook (25 health)\nMae (18 health)","hidden":true}],"require":[{"pattern":"class\\s+Healer\\s*\\(\\s*Character\\s*\\)","message":"`Healer` should inherit `Character`: `class Healer(Character):`."},{"pattern":"def\\s+__init__","max":1,"message":"`Healer` inherits its constructor from `Character`, so it doesn't need one of its own."}],"forbid":[]},{"task":"What does this program print? Type each line it prints.\n","kind":"predict","seed":"class Counter:\n    def __init__(self):\n        self.value = 0\n\n    def add(self):\n        self.value += 1\n\n\nclass StepCounter(Counter):\n    def add_many(self, times):\n        for i in range(times):\n            self.add()\n\n\nsteps = StepCounter()\nsteps.add()\nsteps.add_many(3)\nprint(steps.value)\nplain = Counter()\nplain.add()\nprint(plain.value)\n","solution":"class Counter:\n    def __init__(self):\n        self.value = 0\n\n    def add(self):\n        self.value += 1\n\n\nclass StepCounter(Counter):\n    def add_many(self, times):\n        for i in range(times):\n            self.add()\n\n\nsteps = StepCounter()\nsteps.add()\nsteps.add_many(3)\nprint(steps.value)\nplain = Counter()\nplain.add()\nprint(plain.value)\n","hints":["`StepCounter` has no constructor of its own, so a new step counter starts from `Counter`'s constructor, with `value` 0.","`add_many(3)` calls the inherited `add` three times. Count every `add` that runs on `steps`.","`plain` is a different object, with its own `value`."],"tests":[{"name":"Output","stdin":"","expect":"4\n1","hidden":false}],"require":[],"forbid":[],"lines":["4","1"]}]}