Reading time: 35 minutes
Classes are the building blocks of object oriented programming and since python is an object oriented language it supports classes implicitly. Classes give us a way to model real world objects. They combine data and functions into one entity.
Defining a class
Like functions classes need to be defined before we can use them. Alternatively we can import classes from other files/modules. They are like blueprints from which we can create individual instances of objects.
class PetDog(): """Your companion in good and bad times.""" def __init__(self, name, age): """Give this puppy age and name.""" self.name = name self.age = age
Here we have tried to model a simple dog having two properties name & age. Notice that there is a docstring (text between three quotes) after class name and function definition. These are like comments and describe what the particular part of code does. We can access docstring by using
__doc__ attribute. Try printing out
PetDog.__doc__ in the terminal.
__init__() method and
Methods are just fancy name for functions that belong to a particular class. The
__init__ method (two underscores before and after init) is a special method which is automatically called whenever we make an instance of the class. As the name suggest it initializes the starting conditions. Our definition of init is accepting three arguments i.e., self, name and age.
Self parameter is mandatory in all method definitions and must come before any other parameters. And interestingly we don't need to pass self when calling that method because because self refers to the instance itself. This will become more clear below.
Methods gives us ability to add extra functionality to our classes. Append this at the end of PetDog definition we just wrote.
def sit(self): """Mimic sitting in response to your voice.""" print(self.name + " is now sitting.") def stand(self): """Mimic standing on your command.""" print(self.name + " is now standing")
The two methods defined here allows our dog to sit and stand. Although they just print a sentence as of now, it can be expanded to more practical usage like an animation being played in a video game.
Playing with instances
dog1 = PetDog('Tommy', 6) print(dog1.name + " is " + str(dog1.age) + " years old.")
Calling PetDog returns an instance of PetDog which we store in dog1 variable. Notice that we only passed two arguments to PetDog. Generally class names are capitalised while instances are not. This naming convention helps in distinguishing them. To access a particular attribute we just use the dot notation (like in method definitions above). Methods are also called in similar way.
dog1.sit() # --> Tommy is now sitting. dog1.stand() # --> Tommy is now standing.
We can create multiple instances of each class and they will be independent of each other.
dog2 = PetDog("Jerry", 3) print(dog2.name + " and " + dog1.name + " are now friends.") print("Their age difference is " + str(abs(dog1.age - dog2.age)) + " years.")
Let's say it's the birthday of Tommy (dog1) and we want to increment his age by one. There are multiple ways by which we can do this.
# simply increment age attribute of dog1 dog1.age += 1
This works but it is a little cumbersome to update age manually. Alternatively we can define a method called birthday() that will manage all birthday related tasks for us. Modify our definition of PetDog to add this at the end.
def birthday(self): """Celebrate birthday""" print("Happy Birthday" + self.name + "!") self.age += 1
Now everytime we call birthday on an instance of PetDog it will print a birthday message and increment the age.
print(dog1.age) # --> 6 dog1.birthday() # --> Happy Birthday Tommy! print(dog1.age) # --> 7
Basics of Inheritance
Suppose we want to create a new robotic dog. Our robotic dog will be very similar to real life dog and have features of it's own. But we don't want to start from scratch modelling this robotic dog. Inheritance solves exactly this particular problem. With inheritance we can utilize our PetDog class sort of like a parent while RoboDog will be the child class.
class RoboDog(PetDog): """A new generation of pets.""" def __init__(self, name, age): """Initialize attributes and parent.""" super().__init__(name, age) self.battery = 100 def battery_info(self): print(str(self.battery) + "% battery left.")
This code is similar to previous one except that we specify out parent class (PetDog) in parenthesis of the defination of child class. The init method as usual takes all the required parameters to initialize RoboDog. The super function inside init is our gateway to the parent class. It calls the init method of parent which gives a child all the attributes and methods of parent.
Next we define a new attribute specific to RoboDog and set it's default value to 100% (every new dog we initialize will be fully charged). We alse define a method which shows the current battery left. Let's test out our new class.
dog3 = RoboDog("Chitti", 4) dog3.sit() # --> Chitti is now sitting. dog3.battery_info() # --> 100% battery left.
We can even override the methods from the parent class. Just define the method with same name as of parent and python will ignore the one in parent class.
Inheritance are of different forms. What we saw here was single inheritance where only one parent and child are involved. There is also multiple inheritance where a child can have multiple parent classes.
We can also inherit across multiple levels. A nice example would be to use our RoboDog class as a parent for another class.
Add your own stuff!
Our RoboDog does not do much as of now. Try adding new features to it like:
- a play method where the battery gets depleted each time we play with our dog.
- You can also add a charge_battery method to charge the battery
- An emotion field and associated methods to mimic the mood of your dog