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) # -> '<__main__.Person object at 0x0A...>'
- The output of
print
on objectp1
, 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
, abuilt-in data type
, anotherobject
. 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) # -> '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) # -> '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
describesis 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 classPerson
, to create it’s object.
Extending Built-in Types
Inheritance
feature can be also used to extend the built-in classes likelist
ordict
.- The following example extends
list
and createsEmployeesList
, 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 methodsgetSalary
andgetBonus
. - Definition of
ContractEmployee
class derived fromEmployee
. It overrides functionality ofgetSalary
andgetBonus
methods found in it’s parent classEmployee
.
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 apublic
one.a single underscore
isprivate
, however, still accessible from outside.double underscores
isstrongly private
and not accessible from outside.
Abstraction and Encapsulation Example
empid
attribute ofEmployee
class is made private and is accessible outside the class only using the methodgetEmpid
.
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'