Python – Object Oriented Programming

In this chapter, we are going to use Object Oriented Programming(OOP) features of Python. You will become an expert in Python OOP.

So we’ll cover the following

  1. Review of OOP Terms
  2. Creating a Class
  3. Creating Objects from a Class
  4. Accessing Attributes
  5. Built-in Class Attributes
  6. Garbage Collection in Python
  7. Inheritance in Python
  8. Base Overloading Methods
  9. Operator Overloading
  10. Data Hiding

 

1. Review of OOP Terms

Let’s reviews some of the important OOP terms.

Class – A blueprint for creating new object. A class defines the attributes and behaviors of an object. The attributes are data members (instance and class variables). The behavior is represented by methods(or member functions)

Class Variable – This is a variable that is shared by all objects created from the class. You define class variables inside the class but outside any of the methods.

Data Member – This are variables defined in the class. They are used to hold data associated with the specific class

Function Overloading – You use function overloading to create two functions with same name. But they perform different operations based on its arguments

Instance Variable – These is a variable that is defined in a class but belongs only to the particular instance of the class.

Instance – An object created from a class

Inheritance – The feature that allows a class to inherit the properties of another class. The parent class is called the super class while the child class is called sub class.

Instantiation – The process of creating an instance (object of a class)

Operator Overloading – The process where an operator performs more than one type of operation

 

2. Creating a Class

You create a class using the class statement

'Creating an new Class'
class Student:
   'Common base class for all students'
   noOfStudents = 0

   def __init__(self, regno, firstname, lastname):
      self.regno = regno
      self.firstname = firstname
      self.lastname = lastname
      Student.noOfStudents += 1

   def displayCount(self):
     print("Total Students %d" % Student.noOfStudents)

   def printStudent(self):
      print("Reg. Number: " + self.regno, "Name: " + self.firstname + " " + self.lastname)

Listing 1.0: Definition of the Student Class

 

Let’s understand some parts of this class definition

noOfStudents:  this is a class variable. Therefore it belongs to the class. It’s value is shared by all objects created from this class. You can access it using the class name. For example, Student.noOfStudents. Also, just one copy  exist in memory.

__init__ (): This is a special function. It is called a constructor. It is called when the class is instantiated.

self: this refers to the current class

 

3. Creating Objects from a Class (Instantiation)

Now that we have our class defined, we can then create objects from the class. To do this, you call the class using its name. Then you pass the arguments as defined in the __init__ function.

For example, we create and display two student objects

 

student1  = Student("001", "Solace", "Okeke")
student2  = Student("002", "Treasure", "Munonye")

student1.printStudent()
student2.printStudent()

print("Number of Students: %d" %(Student.noOfStudents))

Listing 1.1: Creating objects and accessing attributes

 

 

4. Accessing Attributes

You can notice that in the Listing 1.1 above, we create two objects, student1 and student2. Then we access the attributes printStudent() and noOfStuents.

But you can also use certain functions defined in Python to access attributes. These functions are given in the table below.

getattr(object, name): used to access the attribute of the object
hasattr(object, name): used to check if an attribute exists
setattr(object, name, value): assigns an attribute. If the attribute does not exits, then  it is created
delattr(object, name): deletes an attribute

 

hasattr(student1, 'grade')                  # returns False
getattr(student1, 'firstname')              # returns Solace

setattr(student1, 'firstname', 'Oleander')  # changes the lastname to Oleander

delattr(student1, 'lastname')               # deletes the lastname attribute

 

 

5. Built-in Class Attribute

Python provides some built-in attributes that you can use for certain operation. Just like normal attributes, these built-in attributes can be accessed the same way.

  • __dict__ : Dictionary that contains the class’s namespace.
  • __doc__ :  The Class documentation string or none, if undefined.
  • __name__ : The name of the Class
  • __module__ :  Name of the module where the class is defined. This attribute is “__main__” in interactive mode.
  • __bases__ :  A tuple that contains the base classes. Listed in the order of their occurrence in the base class list.

We now used these in-built attributes for the Student class−

'Creating an new Class'
class Student:
   'Common base class for all students'
   noOfStudents = 0

   def __init__(self, regno, firstname, lastname):
      self.regno = regno
      self.firstname = firstname
      self.lastname = lastname
      Student.noOfStudents += 1

   def displayCount(self):
     print("Total Students %d" % Student.noOfStudents)

   def printStudent(self):
      print("Reg. Number: " + self.regno, "Name: " + self.firstname + " " + self.lastname)


print("Student.__doc__:", Student.__doc__)
print("Student.__name__:", Student.__name__)
print("Student.__module__:", Student.__module__)
print("Student.__bases__:", Student.__bases__)
print("Student.__dict__:", Student.__dict__)

Listing 1.2: Using inbuilt attributes

 

If you run the code in Listing 1.2, you will have the following output

Student.__doc__: Common base class for all students
Student.__name__: Student
Student.__module__: __main__
Student.__bases__: (<class 'object'>,)
Student.__dict__: {'__module__': '__main__', '__doc__': 'Common base class for all students', 'noOfStudents': 0, '__init__': <function Student.__init__ at 0x000001B37FF81E18>, 'displayCount': <function Student.displayCount at 0x000001B37FF81F28>, 'printStudent': <function Student.printStudent at 0x000001B37FF892F0>, '__dict__': <attribute '__dict__' of 'Student' objects>, '__weakref__': <attribute '__weakref__' of 'Student' objects>}

 

 

6. Garbage Collection in Python

Python uses automatic garbage collection. This means that unneccessary objects are deleted automatically by the Python interpreter. Therefore, memory is freed by reclaiming unused memory blocks.

The Python garbage collector starts running as soon as program execution starts but is actually triggered when no more references to the object. So we can say that the object’s reference count is zero.

An object reference count increases when it is place in a collection or assigned a new name. Similarly, the object reference count decreases when it’s deleted with the del command or its reference is reassigned or it goes out of scope.

How it works is illustrated below:

x = 20      # Create object <20>
y = a       # Increase reference count  of <20> 
z = [b]     # Increase reference count  of <20> 

del x       # Decrease reference. count  of <20>
y = 100     # Decrease reference count  of <20> 
z[0] = -1   # Decrease reference count  of <20>

 

 

7. Inheritance in Python

As you know, inheritance allows you to create a class that inherits from an existing class. You do this by placing the existing class name in brackets after the new class name.

If you do this, then you can use the attributes of the parent class in the child class. The code below illustrates this:

 

class Parent:        # define the parent class
   parentAttr = 100
   def __init__(self):
      print("This is the parent constructor")

   def parentMethod(self):
      print('This is a parent method')

   def setAttr(self, attr):
      Parent.parentAttr = attr

   def getAttr(self):
      print("Parent attribute :", Parent.parentAttr)

class Child(Parent): # define the child class, inherits Parent
   def __init__(self):
      print("This is the child constructor")

   def childMethod(self):
      print('This is a child method')

child1 = Child()          # Create an instance of child
child1.childMethod()      # Child calls its own method
child1.parentMethod()     # Child calls inherited parent's method
child1.setAttr(100)       # Child calls inherited parent's method
child1.getAttr()          # Child also calls inherited parent's method

 

The output of running this code is given below

This is the child constructor
This is a child method
This is a parent method
Parent attribute : 100

 

Multiple Inheritance in Python

Unlike Java, Python supports multiple inheritance. Hence a class can inherit from two or more parents.

To achieve this, simply place the names of the parents in brackets separated by commas. For example, Child in the code below inherits from three parents

 

class Child(Parent1, Parent2, Parent3)

 

issubclass() and isinstance()

The issubclass() method is used to check if a given class is a subclass of  another class

The isinstance() method is used to check if an object is an instance of a given class.

 

Overriding Methods

Overriding occurs when there is a method in the parent and child class that have the same name. In this case,  the method in the child class overrides the method in the parent class. You probably would need to override sometimes to improve the functionality of the method.

 

8. Base Overloading Methods

There are some built-in method that you can always override in your class. These methods are given in the table below.

SN Method and description
1 __init__ ( self [,args…] )

Constructor (with any optional arguments)

2 __del__( self )

Destructor, deletes an object

3 __repr__( self )

Evaluable string representation

4 __str__( self )

Printable string representation

5 __cmp__ ( self, x )

Object comparison

 

 

9. Operator Overloading

As you know, you can define an operator to have more than on functionality. For example, the addition operator(+) add two numbers. However, it would not add two vectors. If you try to use it to add two vectors, then you receive an error.

So to solve this problem, you overload the addition operator(+). You define it to also perform addition on vectors.Simply define the __add__ method in your class.

The code is given below. I recommend you try it yourself. Also remember to watch the video.

 

class Vector:
   def __init__(self, x, y):
      self.x = x
      self.y = y

   def __str__(self):
      return 'Vector (%d, %d)' % (self.x, self.y)

   def __add__(self, vec):
      return Vector(self.x + vec.x, self.y + vec.y)

vector1 = Vector(5,20)
vector2 = Vector(2,10)
print(vector1 + vector2)

 

 

10. Data Hiding in Python

Simply put, data hiding an OOP concept used to hide internal details of a class(attributes, for example).

As such, the attribute is not visible outside the class definition.  To achieve this, you will have to name the attribute with double underscore.

 

class Employee:
    __empCount = 0

    def count(self):
        self.__empCount += 1
        print(self.__empCount)

employee1 = Employee()
employee1.count()
employee1.count()

print(employee1.__empCount)  # This would yeild error

 

When you execute the code above, you will get an error.