Python — Object-Oriented Programming Overview
Did you know that parents would rather have their kids learning Python over French?
That’s what a study conducted by Ocado Technology some time ago in the UK concluded. According to them, 6 out of 10 parents would rather have their children learning Python over French. Interesting, isn’t it?😉
Having that in mind and some spare time in my hands, I have decided to take Python for a spin and revisit the basic key features of the language from an Object-Oriented perspective.
Starting with an interesting question…
Is Python Object-Oriented?
Some authors would say that Python can be considered an object-based language and not really a pure object-oriented language. Mostly because everything is an object in Python but object-oriented programming itself is only an optional feature in the language, as opposed to languages like Java and others.
From that point of view, one could also say that Python is a multi-paradigm language with full support for object-oriented programming, structured programming, and some support for functional programming.
Let’s then take a closer look at how Python implements key O.O. concepts:
Objects
Objects are the main stars of object-oriented programming, meant to represent real-world elements by encapsulating data and behavior together in a single entity. Quite an important role if you ask me!
In Python, everything is an object. That includes anything, even numbers, strings, functions, and modules. Therefore everything can be assigned to a variable.
With Python, we create Objects from Class specifications.
Classes
Classes are the blueprints used for object creation. They define the state an object can have —the data — and what an object can do — the behavior.
If a robot were the object, a Robot class would be its blueprint.
class Robot:
name = "Barry The Robot"
def sayHello(self):
print("Hello, I'm " + self.name)
A Python program will construct these objects from its blueprint whenever a new object is needed or requested. The constructed object will contain all the specified functions and properties.
Methods
Methods specify what the object can do. And in Python, we define a method using the def keyword — just the same as a function, but a method is contained in a class instead. In our previous example, sayHello is a method definition.
All methods must have self as their first parameter.
Self
The self parameter seen in Python programs as the first argument in methods, refers to the instance of the class itself.
It’s important to note that self is just a variable name, and unlike the this keyword of languages like C++, C#, and Java, self can be changed if desired although discouraged.
Here, the most accepted convention is to always use self.
class Robot:
name = "Barry The Robot"
def sayHello(self, personName):
print("Hello " + personName + ", I am " + self.name + "!")
Constructor
Python makes usage of the __init__ method to construct an object in a predefined initial state. The __init__ method is a constructor method that will be executed as soon as an object is instantiated.
As with any other method, a reference to the instance being constructed is passed as its first argument.
class Robot:
def __init__(self):
self.name = "Barry The Robot"
self.isEnabled = False
def sayHello(self, personName):
print("Hello " + personName + ", I am " + self.name + "!")
Encapsulation
Mostly used to bundle and encapsulate behavior and data together, preventing external agents from unintentionally causing side-effects, Python provides us mainly with 3 levels of encapsulation and a few notes:
- private: methods and variables are prefixed with double underscore __
- protected: methods and variables are prefixed with a single underscore _
- public: everything else
class Robot:
def __init__(self):
self.__name = "Barry The Robot"
self.__isEnabled = False
def sayHello(self, personName):
print("Hello " + personName + ", I am " + self.__name + "!")
In the example above, both __name and __isEnabled are private* variables.
*In regards to encapsulation, Python only hides the encapsulated member away, but that member can still be accessed using the class’s parent class name + field name. Following our example, that would be _Robot__name**.
Python never really restricts access to protected or private members. Therefore, nothing is ever really private in Python.
**The above hack is interesting and everything but, now you have to promise to never really use it in any production code!😁
***Seriously, don’t use it ever…
Properties
If access to internal members is needed to expose internal fields to the outside world, Properties can and should be used instead.
In Python, Properties are defined by the usage of the attributes: @property, @setter, and @deleter:
class Robot:
def __init__(self):
self.__name = None
def sayHello(self):
print("Hello, I'm " + self._name )
@property
def name(self):
"""I'm the 'name' property."""
print("getter of name called")
return self.__name
@name.setter
def name(self, value):
print("setter of name called")
self.__name = value
@name.deleter
def name(self):
print("deleter of name called")
del self.__name
Inheritance
Python inheritance will allow us to derive classes from parent classes — a.k.a.: base classes.
To create a subclass from a base class, the subclass must contain a set of parenthesis in front of its declaration name specifying its parent base class:
class FlyingRobot(Robot):
def fly(self):
print("I can fly")
Subclasses will have access to all instance variables and methods from their base class, including private and protected methods.
Interfaces
An interface defines all methods that an object must have, whatever its true type is. The interface will define abstract methods that should be implemented by its sub-type.
The important things here are:
- Python provides us with two different ways of defining interfaces: informal interfaces and formal interfaces
- Python doesn’t contain an
interface
keyword (… I’m looking at you Java and C# folks!)
Informal Interfaces
An informal interface is a Python class that defines methods that should be overridden by its subclasses, but the override is not necessarily enforced.
class Machine:
def sayHello(self) -> str:
"""The machine should say hello in its own language."""
pass
Formal Interfaces — a.k.a.: Abstract Base Classes (ABC)
Formal interfaces are the ones that enforce the implementation. These are created by making usage of the abc
Python module:
import abc
class Machine( abc.ABC ):
@abc.abstractclassmethod
def sayHello(self) -> str:
"""The machine should say hello in its own language."""
pass
Static Methods
Static methods are methods that are not bound to objects, but classes instead. Therefore not mandating that the creation of an object is required in order to execute them.
For defining static methods, the attribute @staticmethod is the one to be used:
class Robot:
def __init__(self):
self.__name = None
def sayHello(self):
print("Hello, I'm " + self.__name )
@property
def name(self):
"""I'm the '__name' property."""
print("getter of name called")
return self.__name
@name.setter
def name(self, value):
print("setter of name called")
self.__name = value
@name.deleter
def name(self):
print("deleter of name called")
del self.__name
@staticmethod
def version():
print("1.0.1")
Final Thoughts
Taking the time to understand how a known concept like object orientation is used in Python and the mindset of the language around it has proved to be very enlightening and satisfying to me.
As it turns out, Python is a simple language, with no much setup needed to get started coding, and simple and clean syntax that looks pretty much like plain simple English. It’s no surprise popularity of the language on scripting, automation, including more recently scientific fields like data analysis, AI, and sometimes a replacement for MATLAB and R.
Back to those parents who would rather have their children studying Python over French(… and having tried French myself and given up too many times 😉), they may have a point after all.
Regarding the object-oriented capabilities of Python, they seem in line with the dynamic nature of the language. Offering most of the core concepts without restricting much on what the coder can do.
That can also be seen by the way the language handles private fields and encapsulation. Not really preventing us from accessing private fields and information, but putting that away or hidden instead. A mindset that is not shared by languages like C++ and Java.
To put it in simple terms with a quote I’ve found on the Python mailing lists …
“…we are all consenting adults here…” — Python Mails