Skip to content

Instantly share code, notes, and snippets.

@Alexhuszagh
Last active August 11, 2017 01:05
Show Gist options
  • Save Alexhuszagh/c73d3f0736d045c10beb26d32a1d2380 to your computer and use it in GitHub Desktop.
Save Alexhuszagh/c73d3f0736d045c10beb26d32a1d2380 to your computer and use it in GitHub Desktop.
A Gentle Introduction to Object-Oriented Programming (OOP)

Before OOP

Traditionally, functions and data were separate entities. Say, for example, I have an employee who would like to change his name, I can do this with an object and a function.

Code

class Employee(object):
    _id = -1
    _name = ""
    _nino = -1

    def __init__(self, id, name, nino):
        self._id = id
        self._name = name
        self._nino = nino
        
def set_name(employee, new_name):
    employee._name = new_name

Example

Bob = Employee(1, "Bob", 111111111)
set_name(Bob, "Robert")

OOP

However, in modern programming languages, we can frequently combine the two: data contains methods to do functional tasks. Using the previous example, we now define a class with a method.

code

class Employee(object):
    _id = -1
    _name = ""
    _nino = -1

    def __init__(self, id, name, nino):
        self._id = id
        self._name = name
        self._nino = nino
        
    def set_name(self, new_name):
        self._name = new_name

Example

Bob = Employee(1, "Bob", 111111111)
Bob.set_name("Robert")

This interface is significantly more elegant, since I can call the set_name member function of Employee, rather than having conflicting functions all at the global scope.

Interfaces

OOP allows us to go one step further: defining interfaces. Let's consider we work at the MI6, and we therefore need to have private and public employees, all of which can be accessed via a common interface. Let's expand on our employee class to set a base class,

class Employee(object):
    _id = -1
    _name = ""
    _nino = -1

    def __init__(self, id, name, nino):
        self._id = id
        self._name = name
        self._nino = nino

    def get_id(self):
        return self._id
        
    def set_id(self, new_id):
        self._id = new_id

    def get_name(self):
        return self._name
        
    def set_name(self, new_name):
        self._name = new_name

    def get_nino(self):
        return self._nino
        
    def set_nino(self, new_nino):
        self._nino = new_nino

I would like my private employees to have a hidden name and nino, using an alias, at least in the accessible records.

class PrivateEmployee(Employee):
    _real_id = 7
    _real_name = "James Bond"
    _real_nino = 111111111

    def __init__(self, id, name, nino):
        self._id = id
        self._name = name
        self._nino = nino

    def get_id(self):
        return self._id
        
    def set_id(self, new_id):
        self._real_id = new_id

    def get_name(self):
        return self._name
        
    def set_name(self, new_name):
        self._real_name = new_name

    def get_nino(self):
        return self._nino
        
    def set_nino(self, new_nino):
        self._real_nino = new_nino

Each setter therefore updates only the private name, but keeps the public name identical, and each getter only accesses the public name. We can therefore use our Employee and PrivateEmployee classes interchangeable.

Abstract Classes

As you may have surmised, OOP modifies interfaces via inheritance: you can inherit from one class and specialize it for a new function. It is therefore frequently useful to have completely abstract classes in mind, which define the methods we need to implement, but do not actually define them.

Let's define an AbstractEmployee class.

from abc import ABCMeta

class AbstractEmployee(metaclass=ABCMeta):
    @abstractmethod
    def get_id(self):
        pass
    
    @abstractmethod
    def set_id(self, new_id):
        pass

    @abstractmethod
    def get_name(self):
        pass
        
    @abstractmethod
    def set_name(self, new_name):
        pass

    @abstractmethod
    def get_nino(self):
        pass
    
    @abstractmethod
    def set_nino(self, new_nino):
        pass

I can now specialize my AbstractEmployee with both PublicEmployee and PrivateEmployee, to differentiate between the two.

class PublicEmployee(AbstractEmployee):
    _id = -1
    _name = ""
    _nino = -1

    def __init__(self, id, name, nino):
        self._id = id
        self._name = name
        self._nino = nino

    def get_id(self):
        return self._id
        
    def set_id(self, new_id):
        self._id = new_id

    def get_name(self):
        return self._name
        
    def set_name(self, new_name):
        self._name = new_name

    def get_nino(self):
        return self._nino
        
    def set_nino(self, new_nino):
        self._nino = new_nino


class PrivateEmployee(AbstractEmployee):
    _real_id = 7
    _real_name = "James Bond"
    _real_nino = 111111111

    def __init__(self, id, name, nino):
        self._id = id
        self._name = name
        self._nino = nino

    def get_id(self):
        return 7
        
    def set_id(self, new_id):
        self._real_id = new_id

    def get_name(self):
        return "James Bond"
        
    def set_name(self, new_name):
        self._real_name = new_name

    def get_nino(self):
        return 111111111
        
    def set_nino(self, new_nino):
        self._real_nino = new_nino
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment