Python – OOPs

Share

Introduction

Here we will discuss about:

  • Classes and Objects in Python
  • Closures and Decorators
  • Descriptors and Properties

Introduction to OOP

Object-oriented programming can model real-life scenarios and suits developing large and complex applications.

Object

In real life, an object is something that you can sense and feel. For example Toys, Bicycles, Oranges and more.
However in Software development, an object is a non tangible entity, which holds some data and is capable of doing certain things.

Class and Object Relationship

Defining Classes

Class
A Class is a template which contains

  • instructions to build an object.
  • methods that can be used by the object to exhibit a specific behaviour.

class keyword is used to define a class in Python.

Syntax

class <ClassName>(<parent1>, ... ):
    class_body

Example

class Person:
    pass
  • Above example defines Person class without any body.

Creating Objects

  • An object is created by calling the class name followed by a pair of parenthesis.
class Person:
    pass
p1 = Person()      # Creating the object 'p1'
print(p1)          # -&gt; '&lt;__main__.Person object at 0x0A...&gt;'
  • The output of print on object p1, tell you what class it belongs to and hints on memory address it is referenced to.

Setting Attributes

  • You can set attributes, one a time, to an instantiated object and access it using the dot notation.
  • The value which is set to an attribute can be anything: a Python primitive, a built-in data type, another object. It can even be a function or a class.

Example

class Person:
    pass
p1 = Person()
p1.fname = 'Jack'
p1.lname = 'Simmons'
print(p1.fname, '-', p1.lname)  # -&gt; 'Jack - Simmons'
  • You can also set multiple attributes, at once, by defining the initializer method, __init__, inside the class.
  • This method is called by default, during an object creation.
  • It takes values passed inside the parenthesis, during an object creation, as it’s arguments.
  • It also takes self as the first argument, which refers to the current object.
  • In the following example, Person class sets two attributes using __init__ method.
class Person:
    pass
p1 = Person()
p1.fname = 'Jack'
p1.lname = 'Simmons'
print(p1.fname, '-', p1.lname)  # -&gt; 'Jack - Simmons'

Documenting a Class

Each class or a method definition can have an optional first line, known as docstring.

Example

class Person:
    'Represents a person.'
    def __init__(self, fname, lname):
        'Initialises two attributes of a person.'
        self.fname = fname
        self.lname = lname

Understanding a Class

Once documented, you can load the script into an interactive interpreter and run help command on Person class.

>>>help(Person)
Help on class Person in module __main__:
class Person(builtins.object)
 |  Represents a person.
 |
 |  Methods defined here:
 |
 |  __init__(self, fname, lname)
 |      Initialises two attributes of a person.
 |
... and more

Inheritance in Python 3

  • Inheritance describes is a kind of relationship between two or more classes, abstracting common details into super class and storing specific ones in the subclass.
  • To create a child class, specify the parent class name inside the pair of parenthesis, followed by it’s name.

Example

class Child(Parent):
   pass
  • Every child class inherits all the behaviours exhibited by their parent class.
  • In Python, every class uses inheritance and is inherited from object by default.
  • Hence, the below two definitions of MySubClass are same.

Definition 1

class MySubClass:
    pass

Definition 2

class MySubClass(object):
    pass
  • object is known as parent or super class.
  • MySubClass is known as child or subclass or derived class.

Inheritance in Action

class Person:
    def __init__(self, fname, lname):
        self.fname = fname
        self.lname = lname
#Employee class is derived from Person.
class Employee(Person):
    all_employees = []
    def __init__(self, fname, lname, empid):
        Person.__init__(self, fname, lname)
        self.empid = empid
        Employee.all_employees.append(self)
p1 = Person('George', 'smith')
print(p1.fname, '-', p1.lname)
e1 = Employee('Jack', 'simmons', 456342)
e2 = Employee('John', 'williams', 123656)
print(e1.fname, '-', e1.empid)
print(e2.fname, '-', e2.empid)

Output


George - smith
Jack - 456342
John - 123656
  • In above example, Employee class utilises __init __ method of parent class Person, to create it’s object.

Extending Built-in Types

  • Inheritance feature can be also used to extend the built-in classes like list or dict.
  • The following example extends list and creates EmployeesList, which can identify employees, having a given search word in their first name.

Example

class EmployeesList(list):
    def search(self, name):
        matching_employees = []
        for employee in self:
            if name in employee.fname:
                matching_employees.append(employee.fname)
        return matching_employees
#EmployeesList object can be used to store all employee objects, just by replacing statement
#all_employees = [] with all_employees = EmployeesList().
class Employee(Person):
    all_employees = EmployeesList()
    def __init__(self, fname, lname, empid):
        Person.__init__(self, fname, lname)
        self.empid = empid
        Employee.all_employees.append(self)
e1 = Employee('Jack', 'simmons', 456342)
e2 = Employee('George', 'Brown', 656721)
print(Employee.all_employees.search('or'))

Output


['George']

Polymorphism in Python 3

Polymorphism allows a subclass to override or change a specific behavior, exhibited by the parent class.
In the below shown example, you will find

  • Improvised Employee class with two methods getSalary and getBonus.
  • Definition of ContractEmployee class derived from Employee. It overrides functionality of getSalary and getBonus methods found in it’s parent class Employee.
class Employee(Person):
    all_employees = EmployeesList ()
    def __init__(self, fname, lname, empid):
        Person.__init__(self, fname, lname)
        self.empid = empid
        Employee.all_employees.append(self)
    def getSalary(self):
        return 'You get Monthly salary.'
    def getBonus(self):
        return 'You are eligible for Bonus.'
class ContractEmployee(Employee):
    def getSalary(self):
        return 'You will not get Salary from Organization.'
    def getBonus(self):
        return 'You are not eligible for Bonus.'
e1 = Employee('Jack', 'simmons', 456342)
e2 = ContractEmployee('John', 'williams', 123656)
print(e1.getBonus())
print(e2.getBonus())

Output


You are eligible for Bonus.
You are not eligible for Bonus.

Abstraction and Encapsulation

  • Abstraction means working with something you know how to use without knowing how it works internally.
  • Encapsulation allows binding data and associated methods together in a unit i.e class.
  • These principles together allows a programmer to define an interface for applications, i.e. to define all tasks the program is capable to execute and their respective input and output data.
  • A good example is a television set. We don’t need to know the inner workings of a TV, in order to use it. All we need to know is how to use the remote control (i.e the interface for the user to interact with the TV).

Abstraction

Encapsulation

Abstracting Data

  • Direct access to data can be restricted by making required attributes or methods private, just by prefixing it’s name with one or two underscores.
  • An attribute or a method starting with:
    • no underscores is a public one.
    • a single underscore is private, however, still accessible from outside.
    • double underscores is strongly private and not accessible from outside.

Abstraction and Encapsulation Example

  • empid attribute of Employee class is made private and is accessible outside the class only using the method getEmpid.
class Employee(Person):
    all_employees = EmployeesList()
    def __init__(self, fname, lname, empid):
        Person.__init__(self, fname, lname)
        self.__empid = empid
        Employee.all_employees.append(self)
    def getEmpid(self):
        return self.__empid
e1 = Employee('Jack', 'simmons', 456342)
print(e1.fname, e1.lname)
print(e1.getEmpid())
print(e1.__empid)

Output


Jack simmons
456342
AttributeError: Employee instance has no attribute '__empid'