Python 2.7 – Fundamentals – Classes & Objects – Basic Theory

What Is a Class

Classes are ways of grouping data together so that they can be manipulated in code as a single unit. Imagine various rooms in a house – bedroom, hall, kitchen, dining room, bathroom. The rooms have individual functionality but they can be grouped together into a single unit called a house. Often in programming you need ways to group data together so that it becomes easier to do calculations or implement logic. Grouping data together is called creating a Class. So in this example, we define a Class called House.

A Class contains two things – methods and class variables.

Methods

Methods are functions which are declared inside the class. For example a BankAccount class can have methods called deposit() and withdraw().

Class Variables

The difference between a normal variable and a class variable is that of visibility. A class variable is available all across the class and all methods in the class can access them. They are also known as Attributes.

Lets define a class called Room . It will have three attributes – name, length in feet and height in feet:

class Room:
 width = 0
 length = 0
 name = ""



kitchen = Room()
kitchen.width = 10
kitchen.length = 12
kitchen.name = "Kitchen"

print kitchen.name, kitchen.length, "x", kitchen.width

The output is given below:

Kitchen 12 x 10

Once a class is defined we can use it by calling it as a function. All attributes and methods in a class can be accessed by prefixing it with the object name.

Lets add a method to print the details of the room:

class Room:
 width = 0
 length = 0
 name = ""
 
 def info(self):
  print self.name, self.length, "x", self.width


kitchen = Room()
kitchen.width = 10
kitchen.length = 12
kitchen.name = "Kitchen"

kitchen.info()


The output is the same as before except now its defined as a method in the class:

Kitchen 12 x 10

You will notice that when we define a method we put self as a parameter, though its not used when the method is called from the object. Python needs a reference to the object passed to its own methods. The self parameter takes care of that.

Lets define another method to calculate area:

class Room:
 width = 0
 length = 0
 name = ""
 
 def info(self):
   print self.name, self.length, "x", self.width 

 def area(self):
   print "area=", self.length * self.width, "sq ft."

kitchen = Room()
kitchen.width = 10
kitchen.length = 12
kitchen.name = "Kitchen"

kitchen.info()
kitchen.area()

The output is given below:

Kitchen 12 x 10
area= 120 sq ft.

What about if we could set the name, length and width while creating the object itself? We can do this by defining our own constructor method. Every class has a default constructor which takes no parameters eg. kitchen = Room() . We can define our own constructors apart from this by using the built-in __init__ method:

class Room:
 width = 0
 length = 0
 name = ""
 
 def __init__(self, name=None, length=None, width=None):
   self.name = name
   self.length = length
   self.width = width

def info(self):
  print self.name, self.length, "x", self.width 

def area(self):
  print "area=", self.length * self.width, "sq ft."

kitchen = Room()
kitchen.width = 10
kitchen.length = 12
kitchen.name = "Kitchen"

kitchen.info()
kitchen.area()

study = Room("Study", 12, 15)
study.info()
study.area()

 

The output is given below:

Kitchen 12 x 10
area= 120 sq ft.
Study 12 x 15
area= 180 sq ft.

You will see that the constructor has three parameters defined apart from self. We have also set up default values for each of them so that the default empty constructor as in kitchen = Room() still works. This is not necessary of course. If you want that the constructor should always have the parameters set then you can remove the default values.

Lets take this example to the next level by defining a class called House which contains Rooms:

class Room:
 width = 0
 length = 0
 name = ""

 def __init__(self, name=None, length=None, width=None):
   self.name = name
   self.length = length
   self.width = width

 def info(self):
   print self.name, self.length, "x", self.width 

 def area(self):
   print "area=", self.length * self.width, "sq ft."

class House:
 rooms = []
 
 def addRoom(self, room):
   self.rooms.append(room)

 def info(self):
  total_area = 0
  for x in self.rooms:
    total_area += (x.width * x.length)
       x.info()
       x.area()
  print "total area of house=", total_area

kitchen = Room()
kitchen.width = 10
kitchen.length = 12
kitchen.name = "Kitchen"

kitchen.info()
kitchen.area()

study = Room("Study", 12, 15)
study.info()
study.area()

print "**************************"
house = House()
house.addRoom(kitchen)
house.addRoom(study)
house.info()

This is the output:

Kitchen 12 x 10
area= 120 sq ft.
Study 12 x 15
area= 180 sq ft.
**************************
Kitchen 12 x 10
area= 120 sq ft.
Study 12 x 15
area= 180 sq ft.
total area of house= 300

 

We define  a class called House which contains Rooms as  a list . Every time we use the addRoom() it adds a Room object to the list.

You can see the advantages of using classes in the example above. It enables us to structure and organise data very efficiently.

In other languages you have the concept of visibility and scope for methods and attributes. For example n C++ and Java you set a method as public,private or protected. Each of these qualifiers sets the visibility of the method. So a public method can be accessed from outside the class, private methods can only be accessed within the class and protected methods are visible only to a subclass (More on that in the next section).

The same goes for attribute variables. But in Python, everything in a class has a public scope. If you still want to designate a method or attribute as private then name it with ___ eg, __mymethod__() . This is just a visible hint to other people working with your code that you do not intend that method to be accessed from outside the class. In certain ways, Python will block access to the method from outside the class because it mangles the method name starting with ___ to something else, but you can still work your way around this restriction. Generally in Python, it is a matter of culture to not directly access any class method or attribute which is surrounded by ___ and ___.

Lets see how to make subclasses now. A subclass is a Class which inherits another Class. The reason for doing so would be to extend the functionality of the class without duplicating code or losing existing classes. So in the example above, we can define a subclass of House called Apartment and another called Bungalow. These two classes will inherit all the data of House and then add their own methods and attributes. Creating subclasses helps us to build a hierarchy of classes which are all connected to each other.

Lets use an example below in which we define   a class called Animal. Then we define a subclass of Animal called Pet:

class Animal:
 species = ""
 name = ""
 
 def __init__(self, species):
   self.species = species
   print "Animal has been initialized"

 def __del__(self):
   print "Animal destroyed"

 def walk(self):
   print self.species ," is walking"

 def run(self):
   print self.species, " is running"

 def domesticate(self, name):
   self.name = name
   if self.species == None:
     print "No species has been set"
   elif self.species.lower() == "dog":
     print self.species," has been domesticated and is called", self.name
   elif self.species.lower() == "cat":
     print self.species, " has been domesticated and is called", self.name
   elif self.species.lower() == "rabbit":
     print self.species, " has been domesticated and is called", self.name

class Pet(Animal):
 owner = ""

 def __init__(self, species, owner):
   self.owner = owner
   Animal.__init__(self, species)
   print "Pet has been initialized"

 def domesticate(self, name):
   Animal.domesticate(self, name)
   print self.owner, " is the owner"


###################

animal = Animal("Dog")
animal.species = "Dog"
print animal.species
animal.walk()
animal.domesticate("Sultan")

animal.species = "cat"
animal.domesticate("Bangles")

rabbit = Animal("rabbit")
rabbit.domesticate("Roger")

pet = Pet("dog", "Amit")
pet.domesticate("Wanda")

print "******************"

print "rabbit is an Animal=",isinstance(rabbit, Animal)
print "pet is an Animal=",isinstance(pet, Pet)
print "rabbit is a Pet=", isinstance(rabbit, Pet)

The output is shown below:

Animal has been initialized
Dog
Dog is walking
Dog has been domesticated and is called Sultan
cat has been domesticated and is called Bangles
Animal has been initialized
rabbit has been domesticated and is called Roger
Animal has been initialized
Pet has been initialized
dog has been domesticated and is called Wanda
Amit is the owner
******************
rabbit is an Animal= True
pet is an Animal= True
rabbit is a Pet= False
Animal destroyed
Animal destroyed
Animal destroyed

A new method which is used above is the __del__() method.  Just like __init__() allows us to set our own constructor __del__() lets us setup our own destructor. The destructor method is called when the class is about to be cleared from memory by Python.

Pet is setup as a subclass of Animal. It automatically inherits all the attributes and methods of Animal. It can even change the functionality of methods in the parent class. So the domesticate() method has been redefined in the Pet class.

For class inheritance to work properly, the subclass must call the corresponding methods of the parent class as required.  This is done by calling the method with the parent class name prefix. In the __init__() constructor of the Pet class, we call the constructor of Animal also. Likewise in the domesticate() method in the Pet class, we call the domesticate() method of the Animal class. This ensures that the parent class is getting correctly updated in sync with the subclass.

The isinstance() function is a built in function which checks the class type of an object. So we see that rabbit is of Animal class, pet is an Animal class also since it inherits the Animal class via the Pet class. The rabbit object is not a Pet class type, since it was created as an Animal class.

 

In the next post , we will look at the basics of working with MySQL databases.

1 Trackback / Pingback

  1. Python 2.7 – Fundamentals – Functions – Truelogic Blog

Leave a Reply

Your email address will not be published.


*