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")
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.
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.
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