Python OOPs Tips
I was exploring bit around Python oops concepts and below few interesting concepts I would like to share, which generally everyone use with any programming.
- . With Python, there is no inbuilt interface class concept, then I was curious to know, how we can handle basic OOPS concept like defining class signature which then should be implemented by child/concreate class. So yes, there options available, which you can use to manage this either using abstractclass and methods or use available interface package.
- . Similarly, second my curiosity was, how I can manage Dependency injection, so that I can keep my code base loosely coupled and I able to find those concept as well to manage DI.
- . Every project has some reusable code base and you surely would not like to repeat same with every python file/classes or projects. So there is concept to expose those reusable code as module and then you can import and use it with rest of files/projects.
- . Other thing, If I want to build some rule on my python class, which I then want to make sure it apply while using it by other child classes, so to do that, I could create Metaclass for the same and write all my rules, I wanted to have for my python class.
Example: Interface/abstract class – usage with Python
Here I have used abstractclass and methods for this example:
The abc package contains some handy tools for defining Abstract Base Classes. ABCs can have one or more abstract method or property. The methods/properties must be overridden by any inheriting class, otherwise it the class can not be instantiated.
Let’s see an example:
from abc import ABC, abstractmethod
class Communication(ABC):
@abstractmethod
def Send(self):
pass
@abstractmethod
def Fetch(self):
pass
class Email(Communication):
def Send(self):
print("Send email test successful")
def Fetch(self):
print("Fetch email test successful")
email = Email()
email.Send()
email.Fetch()
Here with above example, if you do not implement any of the method of Communication class inside your child class i.e. Email, let say you remove "Fetch" method from Email class and then try run this, it will give you error as : "TypeError: Can't instantiate abstract class Email with abstract method Fetch"
If you don’t want your method to have a default implementation, you can leave the function body empty (just write a single pass statement) or raise a NotImplementedError
Handle DI (Dependency Injection):
With below example, I have DbConnectionInterface with connect method and then there is SQLServerConnection class which implement that interface and similarly other class i.e. MongoDBConnection which also implement same interface with connect method. Now I have LoadData class which actually load data from various data source like SQL server, MongoDB or any new database in future, so part of LoadData classs I have pass db_connection as DbConnectionInterface class, so that consumer of this LoadData class will decide which database need to be used. So while calling LoadData class below there I have passed SQLServerConnection class object with first call and then with second call I passed MongoDBConnection class object. So my LoadData class is not dependent on any of my database and it is injecting via __init__ during object creation by consumer.
from abc import ABC, abstractmethod
class DbConnectionInterface(ABC):
@abstractmethod
def connect(self):
pass
class SQLServerConnection(DbConnectionInterface):
def connect(self):
print("Connect to sql db")
class MongoDBConnection(DbConnectionInterface):
def connect(self):
print("Connected to mongo db")
class LoadData():
def __init__(self, db_connection: DbConnectionInterface):
self._db_connection = db_connection
db_connection.connect()
LoadData(SQLServerConnection()) #output: Connect to sql db
LoadData(MongoDBConnection()) #output: Connected to mongo db
Manage reusable code:
To create a module just save the code you want in a file with the file extension should be .py.
Created module - Save this code in a file named mymodule.py
def greeting(name):
print("Hello, " + name)
user = {
"name": "John",
"age": 20
}
Now to consume above created module, created other python file.
Import the module named mymodule, and access the person object or greeting method:
import mymodule
mymodule.greeting("Jonathan") #output: Hello Jonathan
print(mymodule.person1["age"]) #output: 20
NOTE: Now if you have multiple projects like if you are working on microservice architecture, then you should expose reusable module into pypi package and then import directly from python package to use.
Metaclass for managing/creating rules for class to follow while implementing/using:
In this example you have base class which then will be inherit by multiple classes, but you want to apply behaviour that, your base class cannot be inherit with multiple child class i.e. ClassA inherited base class, that is fine and similarly ClassB also inherited base class that is also fine but now, there is classC which is inheriting ClassA and ClassB both together, so as both class has inherit reference of base class, so it should throw error message to not allowing this. You can do all these kind of behaviour change/update using metaclass:
# my metaclass
class MultiBases(type):
# overriding __new__ method with my rules
def __new__(cls, clsname, bases, clsdict):
if len(bases)>1:
raise TypeError("Inherited multiple base classes")
# call __init__ of type class
return super().__new__(cls, clsname, bases, clsdict)
# my base class
class Base(metaclass=MultiBases):
pass
# no error is raised
class A(Base):
pass
# no error is raised
class B(Base):
pass
# This will raise an error (TypeError: Inherited multiple base classes)
class C(A, B):
pass
Categories/Tags: python