OOP TUTORIAL — PYTHON

Basic And Second Generation Inheritance In Python With super().__init__()

Steven Zych
6 min readJan 17, 2021

What Is OOP?

Before we get into the meat of this lesson, there’s a few things that need defining. Obviously the terms in the title need it, but so too does “object-oriented programming” (OOP), the topic within which all of this sits.

OOP is a way of thinking about (and doing) code that takes all these disparate data structures and functions and gloms them into one thing. It’s the difference between having, for example, all your functions and data about dogs in different places and sticking them all together in one. Doing the latter would be achieved through creating a class, which is like a blueprint for the objects we use in OOP.

What Is Inheritance?

Remember how I said we could make a “class” pertaining to dogs? What if we want to have a class pertaining to lions, too? Cheetahs? Other terrestrial critters? Well then we’d have to program another class for each of these examples, and our code could become quite redundant.

If we’re trying to model the behavior of these creatures, there’s a lot of overlap. The Dog class, for starters, likely has methods (class-specific functions) for things like sleep(), defecate(), eat(), and so on, but all the other animals do these things too. Instead of rewriting these methods for every new mammalian class we make, we could rewind a bit and start instead with a class named Mammal, or even just Animal if we’re going for much broader strokes.

In either case, the shared methods that are used by all these forthcoming “children” should be put in the more general class Mammal. Then, when we write the Dog and Lion and Cheetah classes, we have them “inherit” those methods from the “parent” class. Simply put, we make a generic blueprint first, then make specific cases and only add the case-specific information.

The same is true with attributes, which are the data points of an object. For the animals, these could be things like height, weight, or fur_color.

Inheritance Examples

Making A Parent Class

Okay so we’ve got the abstract theory in place, but what does this look like in practice? For examples, I’ll be going off this repository I’m working on to simulate an RPG inventory system. No knowledge of video games necessary to follow along.

Image from stefanonsoftware.com

To begin, let’s make our most general class. We want an inventory system that stores different types of items. A class simply named Item seems like a good enough place to start. Let’s keep it simple, and only have class Item store one piece of data, slots — or, the amount of inventory space an item takes up:

class Item:    def __init__(self, slots = 1):    self.slots = slots

Line by line, here’s what we’re doing:

  • class Item: tells Python we want to define a class named Item. Notice the lack of () which are mandatory in all function definitions. Also notice that it’s capitalized, which isn’t a programmatic necessity, but part of Pythonic cultural conventions.
  • def __init__(self, slots = 1): tells Python what information we want to create about the class when an object is initialized. What do we need to know about an item when we create that item in our program?
  • self.slots = slots is class-specific language for saying that this specific Item object’s slots are equal to what is passed in as “slots” on initialization. Redundant, I know, but that’s that.

Great! We have a class named Item that does the bare minimum. We could make an Item like this:

chocolate_bar_1 = Item()

Or like this:

chocolate_bar_2 = Item(slots = 2)

Just like in functions, if we set an argument equal to something in the definitory language, it will default to that value when called. This means that the first chocolate bar has 1 slot, just like it is set to default to in the class’s __init__() method:

chocolate_bar_1.slots1

Okay, so we’ve got a basic class working, but how do we build on this? Let’s say the next type of Item we want to make for our RPG is Weapon. We can define this class as a subclass of Item.

Basic Inheritance

So, before we write class Weapon, let’s think about what all weapons have in common — They deal damage. For simpleness sake, that’s the only thing we’re going to program them to do. But before we can do that, we’ve got to define our __init__() method. But! Since this class will be the child of class Item, we do it a little differently:

class Weapon(Item):    def __init__(self, slots = 2, damage = 1, element = “normal”):        super().__init__(slots)        self.damage = damage        self.element = element

Line by line:

  • class Weapon(Item): tells Python we’re defining class Weapon and that it is the child of Item, notice the parentheses this time!
  • def __init__(self, slots = 2, damage = 1, element = “normal”): does the same as before, but with the addition of a few attributes that weren’t present in the parent class.
  • super().__init__(slots) is the part that actually “does” the inheritance. This just means “Use the parent’s protocol for initializing slots.” Here it doesn’t save us any lines, but in more complicated examples, it saves a lot of headache with rewriting initialization stuff.
  • self.damage = damage and self.element = element are doing the same as self.slots did in the previous example.

Simple enough, but let’s add a method! Right below that we’re adding the Weapon-specific method do_damage(). This code is being simplified for the lesson’s sake, but feel free to check out the full thing on the repo. (Nothing I’ve omitted is general enough to matter for you to learn.)

def do_damage(self, target):    target.health -= self.damage

Two things to note here:

  • First, class methods must always be passed the argument self first. This lets the class know that it’s their own version of this method that’s being called.
  • Second, the same is true when referencing attributes. Notice how we called self.damage and not just damage? Not doing this would throw an error that damage was never defined.

We have a method. We have a subclass. We have a desire for more!

“Second Generation” Inheritance

I haven’t seen anyone else call it this, and honestly I’m not sure if there’s an agreed-upon term for this, but I’m talking about subclasses of subclasses and so on. What we’re doing here is making a subclass of Weapon called Blaster. Lazer rifles, bazookas, wild west revolvers. Anything that shoots will be counted as a Blaster.

Another second-generation class would be something like Sword or MeleeWeapon. By having defined do_damage() previously in Weapon, we don’t need to rewrite in every sub-subclass, and any time we want to update it, we only have to do so in one place!

For the Blaster class, we’re adding attributes and a method:

class Blaster(Weapon):    def __init__(self, slots = 2, damage = 1, element = “normal”, mag = 8):        super().__init__(slots, damage, element)        self.range_ = range_        self.mag = mag        self.mag_status = mag        self.ammo = ammo        self.accuracy = accuracy    def shoot(self, target):        # Logic to determine if target in range, if you hit, etc.        # Removed for lesson simplicity        # Deal damage        self.do_damage(target)        # Decrement ammo        self.mag_status -= 1        print(“Successful Hit”)

So here, everything in __init__() is logically the same as it was in Weapon, as is the class Blaster(Weapon): portion. You’ll notice, however, that this time we’re inheriting three attributes from Weapon (slots, damage, element) as opposed to just one (slots).

The method shoot(), which has been simplified, contains the line self.do_damage(target). do_damage(), however, has not been defined within the Blaster class. Normally this would throw an error, but since we’re inheriting from the Weapon class, we inherit this method too, even without explicitly saying so. When we make that call to self.do_damage() it’s like we’re calling the Weapon class and asking how it’s done.

In Conclusion

You’ve now learned how to enact Python’s super().__init__() to perform inheritance, and even how to do so on a second-generation scale. Now go get coding, and show me what you make! I’m accessible on Twitter at @zych_steven. The full repository with much more complicated code can be found here if you wanna dive into this inventory system, or drop a suggestion or two! Thanks for reading! :)

--

--