Learning PYTHON the OO way
Many beginners get mislead by the name ‘Python Scripts’ forcing them to think Python is just another scripting language with not much of object oriented concepts included or at least not in the traditional Object Oriented sense(like how Javascript deals with OO concepts). This misinformation makes them assume that learning Python is an arduous task as they have to learn all the programming concepts from scratch. That’s the biggest misconception one could have. For all those who have been under the dark all these days or years, “PYTHON has always been object oriented” since its inception.
I am pretty sure many OO developers out there would heave a huge sigh of relief with this revelation. This basic understanding indeed helped me get things rolling with Python development sooner than I thought.
Being an Object Oriented developer myself for so many years, I jumped at the very opportunity of working on the OO concepts yet again albeit in a completely different programming world. Instead of simply reading around aimlessly on the internet, I decided to start my journey from a rather familiar territory of Object Oriented Programming in the Python World.
As we all know, the four many pillars to Object Oriented Programming are:
- Encapsulation
- Abstraction
- Polymorphism
- Inheritance
And in this blog post, I will try to explain the above concepts in my own words with code snippets to show how PYTHON addresses each of them in it’s own unique way.
Encapsulation
Encapsulation is a mechanism wherein an “Entity” clubs together the “State” and its “Behavior” into one single encapsulated unit which shields the data within from unauthorized access.
In Python terminologies(or any other OOP language for that matter) the entity is analogous to a ‘Class’, the state to the ‘Attributes’ of a class and the behavior is analogous to the ‘Methods’ that operate and manipulate the data within the class.
It’s also important to understand the difference between class level attributes and the instance level attributes when we talk about Encapsulation in Python. The class level attributes are essentially the ones which are shared between all the instances of a particular class and the instance level attributes specifically apply to only the instances.
In the above code snippet a class named ‘Encapsulation’ is created with a constructor which initializes the state of the class with 2 class level attributes. This class binds the attributes and the methods(printState) into one single encapsulated unit.
If you observe the line 22 & 26 the distinction between the class level attributes and instance level attributes becomes clear.
Abstraction
Abstraction is a mechanism of hiding / abstracting all the irrelevant / private or protected details of an entity from the outside world and only exposing the ones that are needed by using appropriate access specifiers like private, public, protected.
Unlike its OOP counterparts like .Net or Java, Python does not support access specifiers. Everything is public by default. Although Python provides a way of protecting the private attributes & methods, the classification merely is based on naming conventions and no restrictions are imposed at the compiler level while accessing them.
Python recommends to prefix characters to the names to specify the access levels.
- Names without prefixes are PUBLIC
- Names with ‘_’(single underscore) are PROTECTED (However, they are still accessible like public attributes)
- Names with ‘__’(double underscore) are PRIVATE (These are strictly private though)
The expectation is to use the attributes / methods prefixed with ‘_’ judiciously based on the directives specified in the documentation of the code.
As you can see in Line 44 & 48, the calls to the protected method and attribute do not throw any errors. However, the accesses to private attributes / methods are safeguarded and appropriate errors are thrown.
Inheritance
Inheritance is a mechanism of modularizing the code by introducing hierarchical relations between entities(classes) in the code thereby reducing code duplication(redundancy) and increasing code reusability. It paves the way for dynamically switching behaviors based on a concept called runtime polymorphism(explained in the next section)
Python facilitates inheritance in the same way as any other OO language by allowing creation of Super class( Parent class) — Sub class(Child class) relationships between classes.
In the above code as you can see, the ‘InheritanceDerived’ class is derived from ‘InheritanceBase’ in the same way as in any other programming language. Couple of things worth noting here are jotted below:
- In Line 29, the same instance of the array is pointing to both base and derived classes which is a classic usage of Inheritance in the OO world
- In Line 34, both the instances(Base & Derived) are internally instances of InheritanceBase which further justifies the previous point. So, an instance of base can point to an instance of derived but the vice-versa is not true(Line 37)
- The derived instance can access any public/protected members of the base class which becomes evident from line 41 & 45. However, the other way round is not true
- The usage of the base constructor in the derived class constructor is also something to take a look at(Line 22).
Polymorphism
Polymorphism is a concept where an entity morphs its behavior under different contexts. This change in context can be static or dynamic based on when the compiler identifies this dynamicity. The former is called ‘Compile time polymorphism’(method overloading) and the latter ‘Runtime polymorphism’(method overriding)
Python supports both the behaviors. Although the method overriding is implemented on the same lines as any other OO language, method overloading implementation slightly differs from Java and resembles the .Net framework implementation of default & dynamic parameters.
The 2 forms of polymorphism supported are implemented in the code above. Like in the previous example the instances array(Ln no 37) holds the instances of both the base and derived objects. The following points throw some light on their implementation:
- Ln. No 42 in the classical example of method overriding(Runtime polymorphism) where the same instance invokes different methods based on the type of instance it points to.
- Ln. No.s 46 & 50 show how the method overloading is implemented using default parameters
- Ln. No.s 54 & 58 display another way of implementing method overloading using the concept of dynamic parameters.
The basic understanding of these concepts should help any developer kick start Python development in no time. It definitely made my task of learning Python way more easier than I initially thought. Now it’s time for me to dig my hands deeper and start learning the advanced concepts to unleash Python’s true potential.
So, happy Pythoning:)